Unverified Commit 67027c0d authored by Marcel Stör's avatar Marcel Stör Committed by GitHub
Browse files

Merge pull request #2340 from nodemcu/dev

2.2 master snap
parents 5073c199 18f33f5f
...@@ -270,7 +270,7 @@ s32_t spiffs_probe( ...@@ -270,7 +270,7 @@ s32_t spiffs_probe(
s32_t res; s32_t res;
u32_t paddr; u32_t paddr;
spiffs dummy_fs; // create a dummy fs struct just to be able to use macros spiffs dummy_fs; // create a dummy fs struct just to be able to use macros
memcpy(&dummy_fs.cfg, cfg, sizeof(spiffs_config)); _SPIFFS_MEMCPY(&dummy_fs.cfg, cfg, sizeof(spiffs_config));
dummy_fs.block_count = 0; dummy_fs.block_count = 0;
// Read three magics, as one block may be in an aborted erase state. // Read three magics, as one block may be in an aborted erase state.
...@@ -713,8 +713,8 @@ s32_t spiffs_populate_ix_map(spiffs *fs, spiffs_fd *fd, u32_t vec_entry_start, u ...@@ -713,8 +713,8 @@ s32_t spiffs_populate_ix_map(spiffs *fs, spiffs_fd *fd, u32_t vec_entry_start, u
s32_t res; s32_t res;
spiffs_ix_map *map = fd->ix_map; spiffs_ix_map *map = fd->ix_map;
spiffs_ix_map_populate_state state; spiffs_ix_map_populate_state state;
vec_entry_start = MIN((map->end_spix - map->start_spix + 1) - 1, (s32_t)vec_entry_start); vec_entry_start = MIN((u32_t)(map->end_spix - map->start_spix), vec_entry_start);
vec_entry_end = MAX((map->end_spix - map->start_spix + 1) - 1, (s32_t)vec_entry_end); vec_entry_end = MAX((u32_t)(map->end_spix - map->start_spix), vec_entry_end);
if (vec_entry_start > vec_entry_end) { if (vec_entry_start > vec_entry_end) {
return SPIFFS_ERR_IX_MAP_BAD_RANGE; return SPIFFS_ERR_IX_MAP_BAD_RANGE;
} }
...@@ -877,8 +877,6 @@ s32_t spiffs_page_delete( ...@@ -877,8 +877,6 @@ s32_t spiffs_page_delete(
spiffs *fs, spiffs *fs,
spiffs_page_ix pix) { spiffs_page_ix pix) {
s32_t res; s32_t res;
spiffs_page_header hdr;
hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED);
// mark deleted entry in source object lookup // mark deleted entry in source object lookup
spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED; spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED;
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE,
...@@ -892,11 +890,18 @@ s32_t spiffs_page_delete( ...@@ -892,11 +890,18 @@ s32_t spiffs_page_delete(
fs->stats_p_allocated--; fs->stats_p_allocated--;
// mark deleted in source page // mark deleted in source page
u8_t flags = 0xff;
#if SPIFFS_NO_BLIND_WRITES
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags),
sizeof(flags), &flags);
SPIFFS_CHECK_RES(res);
#endif
flags &= ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED);
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE,
0, 0,
SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags),
sizeof(u8_t), sizeof(flags), &flags);
(u8_t *)&hdr.flags);
return res; return res;
} }
...@@ -942,7 +947,7 @@ s32_t spiffs_object_create( ...@@ -942,7 +947,7 @@ s32_t spiffs_object_create(
strncpy((char*)oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN); strncpy((char*)oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN);
#if SPIFFS_OBJ_META_LEN #if SPIFFS_OBJ_META_LEN
if (meta) { if (meta) {
memcpy(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN);
} else { } else {
memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN);
} }
...@@ -1006,7 +1011,7 @@ s32_t spiffs_object_update_index_hdr( ...@@ -1006,7 +1011,7 @@ s32_t spiffs_object_update_index_hdr(
} }
#if SPIFFS_OBJ_META_LEN #if SPIFFS_OBJ_META_LEN
if (meta) { if (meta) {
memcpy(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN);
} }
#else #else
(void) meta; (void) meta;
...@@ -1048,34 +1053,66 @@ void spiffs_cb_object_event( ...@@ -1048,34 +1053,66 @@ void spiffs_cb_object_event(
spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG; spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG;
u32_t i; u32_t i;
spiffs_fd *fds = (spiffs_fd *)fs->fd_space; spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
SPIFFS_DBG(" CALLBACK %s obj_id:"_SPIPRIid" spix:"_SPIPRIsp" npix:"_SPIPRIpg" nsz:"_SPIPRIi"\n", (const char *[]){"UPD", "NEW", "DEL", "MOV", "HUP","???"}[MIN(ev,5)],
obj_id_raw, spix, new_pix, new_size);
for (i = 0; i < fs->fd_count; i++) { for (i = 0; i < fs->fd_count; i++) {
spiffs_fd *cur_fd = &fds[i]; spiffs_fd *cur_fd = &fds[i];
#if SPIFFS_TEMPORAL_FD_CACHE if ((cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; // fd not related to updated file
if (cur_fd->score == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; #if !SPIFFS_TEMPORAL_FD_CACHE
#else if (cur_fd->file_nbr == 0) continue; // fd closed
if (cur_fd->file_nbr == 0 || (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue;
#endif #endif
if (spix == 0) { if (spix == 0) { // object index header update
if (ev != SPIFFS_EV_IX_DEL) { if (ev != SPIFFS_EV_IX_DEL) {
SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" objix_hdr_pix to "_SPIPRIpg", size:"_SPIPRIi"\n", cur_fd->file_nbr, cur_fd->obj_id, new_pix, new_size); #if SPIFFS_TEMPORAL_FD_CACHE
if (cur_fd->score == 0) continue; // never used fd
#endif
SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid"(fdoffs:"_SPIPRIi" offs:"_SPIPRIi") objix_hdr_pix to "_SPIPRIpg", size:"_SPIPRIi"\n",
SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, cur_fd->fdoffset, cur_fd->offset, new_pix, new_size);
cur_fd->objix_hdr_pix = new_pix; cur_fd->objix_hdr_pix = new_pix;
if (new_size != 0) { if (new_size != 0) {
// update size and offsets for fds to this file
cur_fd->size = new_size; cur_fd->size = new_size;
u32_t act_new_size = new_size == SPIFFS_UNDEFINED_LEN ? 0 : new_size;
#if SPIFFS_CACHE_WR
if (act_new_size > 0 && cur_fd->cache_page) {
act_new_size = MAX(act_new_size, cur_fd->cache_page->offset + cur_fd->cache_page->size);
}
#endif
if (cur_fd->offset > act_new_size) {
cur_fd->offset = act_new_size;
}
if (cur_fd->fdoffset > act_new_size) {
cur_fd->fdoffset = act_new_size;
}
#if SPIFFS_CACHE_WR
if (cur_fd->cache_page && cur_fd->cache_page->offset > act_new_size+1) {
SPIFFS_CACHE_DBG("CACHE_DROP: file trunced, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix);
spiffs_cache_fd_release(fs, cur_fd->cache_page);
}
#endif
} }
} else { } else {
// removing file
#if SPIFFS_CACHE_WR
if (cur_fd->file_nbr && cur_fd->cache_page) {
SPIFFS_CACHE_DBG("CACHE_DROP: file deleted, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix);
spiffs_cache_fd_release(fs, cur_fd->cache_page);
}
#endif
SPIFFS_DBG(" callback: release fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix);
cur_fd->file_nbr = 0; cur_fd->file_nbr = 0;
cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED; cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED;
} }
} } // object index header update
if (cur_fd->cursor_objix_spix == spix) { if (cur_fd->cursor_objix_spix == spix) {
if (ev != SPIFFS_EV_IX_DEL) { if (ev != SPIFFS_EV_IX_DEL) {
SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", cur_fd->file_nbr, cur_fd->obj_id, spix, new_pix); SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix);
cur_fd->cursor_objix_pix = new_pix; cur_fd->cursor_objix_pix = new_pix;
} else { } else {
cur_fd->cursor_objix_pix = 0; cur_fd->cursor_objix_pix = 0;
} }
} }
} } // fd update loop
#if SPIFFS_IX_MAP #if SPIFFS_IX_MAP
...@@ -1087,7 +1124,7 @@ void spiffs_cb_object_event( ...@@ -1087,7 +1124,7 @@ void spiffs_cb_object_event(
if (cur_fd->file_nbr == 0 || if (cur_fd->file_nbr == 0 ||
cur_fd->ix_map == 0 || cur_fd->ix_map == 0 ||
(cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue;
SPIFFS_DBG(" callback: map ix update fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp"\n", cur_fd->file_nbr, cur_fd->obj_id, spix); SPIFFS_DBG(" callback: map ix update fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix);
spiffs_update_ix_map(fs, cur_fd, spix, objix); spiffs_update_ix_map(fs, cur_fd, spix, objix);
} }
} }
...@@ -1164,7 +1201,7 @@ s32_t spiffs_object_open_by_page( ...@@ -1164,7 +1201,7 @@ s32_t spiffs_object_open_by_page(
SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0);
SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", fd->file_nbr, fd->obj_id); SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", SPIFFS_FH_OFFS(fs, fd->file_nbr), fd->obj_id);
return res; return res;
} }
...@@ -1275,7 +1312,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { ...@@ -1275,7 +1312,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
// quick "load" of new object index page // quick "load" of new object index page
memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs));
memcpy(fs->work, &p_hdr, sizeof(spiffs_page_header)); _SPIFFS_MEMCPY(fs->work, &p_hdr, sizeof(spiffs_page_header));
spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work,
SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0);
SPIFFS_DBG("append: "_SPIPRIid" create objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id SPIFFS_DBG("append: "_SPIPRIid" create objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id
...@@ -2222,7 +2259,7 @@ s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) { ...@@ -2222,7 +2259,7 @@ s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) {
} }
} }
// find the free fd with least score // find the free fd with least score or name match
for (i = 0; i < fs->fd_count; i++) { for (i = 0; i < fs->fd_count; i++) {
spiffs_fd *cur_fd = &fds[i]; spiffs_fd *cur_fd = &fds[i];
if (cur_fd->file_nbr == 0) { if (cur_fd->file_nbr == 0) {
......
...@@ -141,6 +141,22 @@ ...@@ -141,6 +141,22 @@
#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) #define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0)
#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) #define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1)
#if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__)
/* For GCC, clang and TI compilers */
#define SPIFFS_PACKED __attribute__((packed))
#elif defined(__ICCARM__) || defined(__CC_ARM)
/* For IAR ARM and Keil MDK-ARM compilers */
#define SPIFFS_PACKED
#else
/* Unknown compiler */
#define SPIFFS_PACKED
#endif
#if SPIFFS_USE_MAGIC #if SPIFFS_USE_MAGIC
#if !SPIFFS_USE_MAGIC_LENGTH #if !SPIFFS_USE_MAGIC_LENGTH
#define SPIFFS_MAGIC(fs, bix) \ #define SPIFFS_MAGIC(fs, bix) \
...@@ -242,6 +258,15 @@ ...@@ -242,6 +258,15 @@
#define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \ #define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \
( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) ) ( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) )
#if SPIFFS_FILEHDL_OFFSET
#define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0)
#define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0)
#else
#define SPIFFS_FH_OFFS(fs, fh) (fh)
#define SPIFFS_FH_UNOFFS(fs, fh) (fh)
#endif
#define SPIFFS_OP_T_OBJ_LU (0<<0) #define SPIFFS_OP_T_OBJ_LU (0<<0)
#define SPIFFS_OP_T_OBJ_LU2 (1<<0) #define SPIFFS_OP_T_OBJ_LU2 (1<<0)
#define SPIFFS_OP_T_OBJ_IX (2<<0) #define SPIFFS_OP_T_OBJ_IX (2<<0)
...@@ -430,7 +455,7 @@ typedef struct { ...@@ -430,7 +455,7 @@ typedef struct {
spiffs_span_ix cursor_objix_spix; spiffs_span_ix cursor_objix_spix;
// current absolute offset // current absolute offset
u32_t offset; u32_t offset;
// current file descriptor offset // current file descriptor offset (cached)
u32_t fdoffset; u32_t fdoffset;
// fd flags // fd flags
spiffs_flags flags; spiffs_flags flags;
...@@ -455,7 +480,7 @@ typedef struct { ...@@ -455,7 +480,7 @@ typedef struct {
// page header, part of each page except object lookup pages // page header, part of each page except object lookup pages
// NB: this is always aligned when the data page is an object index, // NB: this is always aligned when the data page is an object index,
// as in this case struct spiffs_page_object_ix is used // as in this case struct spiffs_page_object_ix is used
typedef struct __attribute(( packed )) { typedef struct SPIFFS_PACKED {
// object id // object id
spiffs_obj_id obj_id; spiffs_obj_id obj_id;
// object span index // object span index
...@@ -465,7 +490,7 @@ typedef struct __attribute(( packed )) { ...@@ -465,7 +490,7 @@ typedef struct __attribute(( packed )) {
} spiffs_page_header; } spiffs_page_header;
// object index header page header // object index header page header
typedef struct __attribute(( packed )) typedef struct SPIFFS_PACKED
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES #if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
__attribute(( aligned(sizeof(spiffs_page_ix)) )) __attribute(( aligned(sizeof(spiffs_page_ix)) ))
#endif #endif
...@@ -487,7 +512,7 @@ typedef struct __attribute(( packed )) ...@@ -487,7 +512,7 @@ typedef struct __attribute(( packed ))
} spiffs_page_object_ix_header; } spiffs_page_object_ix_header;
// object index page header // object index page header
typedef struct __attribute(( packed )) { typedef struct SPIFFS_PACKED {
spiffs_page_header p_hdr; spiffs_page_header p_hdr;
u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))]; u8_t _align[4 - ((sizeof(spiffs_page_header)&3)==0 ? 4 : (sizeof(spiffs_page_header)&3))];
} spiffs_page_object_ix; } spiffs_page_object_ix;
...@@ -794,4 +819,24 @@ s32_t spiffs_page_consistency_check( ...@@ -794,4 +819,24 @@ s32_t spiffs_page_consistency_check(
s32_t spiffs_object_index_consistency_check( s32_t spiffs_object_index_consistency_check(
spiffs *fs); spiffs *fs);
// memcpy macro,
// checked in test builds, otherwise plain memcpy (unless already defined)
#ifdef _SPIFFS_TEST
#define _SPIFFS_MEMCPY(__d, __s, __l) do { \
intptr_t __a1 = (intptr_t)((u8_t*)(__s)); \
intptr_t __a2 = (intptr_t)((u8_t*)(__s)+(__l)); \
intptr_t __b1 = (intptr_t)((u8_t*)(__d)); \
intptr_t __b2 = (intptr_t)((u8_t*)(__d)+(__l)); \
if (__a1 <= __b2 && __b1 <= __a2) { \
printf("FATAL OVERLAP: memcpy from %lx..%lx to %lx..%lx\n", __a1, __a2, __b1, __b2); \
ERREXIT(); \
} \
memcpy((__d),(__s),(__l)); \
} while (0)
#else
#ifndef _SPIFFS_MEMCPY
#define _SPIFFS_MEMCPY(__d, __s, __l) do{memcpy((__d),(__s),(__l));}while(0)
#endif
#endif //_SPIFFS_TEST
#endif /* SPIFFS_NUCLEUS_H_ */ #endif /* SPIFFS_NUCLEUS_H_ */
#############################################################
# 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 = libsqlite3.a
endif
STD_CFLAGS= -std=gnu11 -Wimplicit -Wno-undef -include config_ext.h
#############################################################
# 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 ../platform
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the `fdatasync' function. */
/* #undef HAVE_FDATASYNC */
/* Define to 1 if you have the `gmtime_r' function. */
#define HAVE_GMTIME_R 1
/* Define to 1 if the system has the type `int16_t'. */
#define HAVE_INT16_T 1
/* Define to 1 if the system has the type `int32_t'. */
#define HAVE_INT32_T 1
/* Define to 1 if the system has the type `int64_t'. */
#define HAVE_INT64_T 1
/* Define to 1 if the system has the type `int8_t'. */
#define HAVE_INT8_T 1
/* Define to 1 if the system has the type `intptr_t'. */
/* #undef HAVE_INTPTR_T */
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `isnan' function. */
#define HAVE_ISNAN 1
/* Define to 1 if you have the `localtime_r' function. */
/* #undef HAVE_LOCALTIME_R */
/* Define to 1 if you have the `localtime_s' function. */
/* #undef HAVE_LOCALTIME_S */
/* Define to 1 if you have the <malloc.h> header file. */
#define HAVE_MALLOC_H 1
/* Define to 1 if you have the `malloc_usable_size' function. */
/* #undef HAVE_MALLOC_USABLE_SIZE */
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the pread() function. */
#define HAVE_PREAD 1
/* Define to 1 if you have the pread64() function. */
#define HAVE_PREAD64 1
/* Define to 1 if you have the pwrite() function. */
#define HAVE_PWRITE 1
/* Define to 1 if you have the pwrite64() function. */
#define HAVE_PWRITE64 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the strchrnul() function */
#define HAVE_STRCHRNUL 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if the system has the type `uint16_t'. */
#define HAVE_UINT16_T 1
/* Define to 1 if the system has the type `uint32_t'. */
#define HAVE_UINT32_T 1
/* Define to 1 if the system has the type `uint64_t'. */
#define HAVE_UINT64_T 1
/* Define to 1 if the system has the type `uint8_t'. */
#define HAVE_UINT8_T 1
/* Define to 1 if the system has the type `uintptr_t'. */
/* #undef HAVE_UINTPTR_T */
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to 1 if you have the `usleep' function. */
#define HAVE_USLEEP 1
/* Define to 1 if you have the utime() library function. */
#define HAVE_UTIME 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */
#define PACKAGE_NAME "sqlite"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "sqlite 3.19.3"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "sqlite"
/* Define to the version of this package. */
#define PACKAGE_VERSION "3.19.3"
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
#define BUILD_sqlite -DNDEBUG
#define _HAVE_SQLITE_CONFIG_H
#define SQLITE_CORE 1
#define SQLITE_NO_SYNC 1
#define YYSTACKDEPTH 20
#define SQLITE_TEMP_STORE 3
#define SQLITE_SYSTEM_MALLOC 1
#define SQLITE_OS_OTHER 1
#define SQLITE_THREADSAFE 0
#define SQLITE_MUTEX_APPDEF 1
#define SQLITE_SECURE_DELETE 0
#define SQLITE_DISABLE_LFS 1
#define SQLITE_DISABLE_DIRSYNC 1
#define SQLITE_DISABLE_FTS3_UNICODE 1
#define SQLITE_DISABLE_FTS4_DEFERRED 1
#define SQLITE_LIKE_DOESNT_MATCH_BLOBS 1
#define SQLITE_DEFAULT_CACHE_SIZE -1
#define SQLITE_DEFAULT_MEMSTATUS 0
#define SQLITE_DEFAULT_MMAP_SIZE 0
#define SQLITE_DEFAULT_LOCKING_MODE 1
#define SQLITE_DEFAULT_LOOKASIDE 512,125
#define SQLITE_DEFAULT_PAGE_SIZE 4096
#define SQLITE_POWERSAFE_OVERWRITE 1
#define SQLITE_MAX_EXPR_DEPTH 0
#define SQLITE_OMIT_ALTERTABLE 1
#define SQLITE_OMIT_ANALYZE 1
#define SQLITE_OMIT_ATTACH 1
#define SQLITE_OMIT_AUTHORIZATION 1
#define SQLITE_OMIT_AUTOINCREMENT 1
#define SQLITE_OMIT_AUTOMATIC_INDEX 1
#define SQLITE_OMIT_AUTORESET 1
#define SQLITE_OMIT_AUTOVACUUM 1
#define SQLITE_OMIT_BETWEEN_OPTIMIZATION 1
#define SQLITE_OMIT_BLOB_LITERAL 1
#define SQLITE_OMIT_BTREECOUNT 1
#define SQLITE_OMIT_BUILTIN_TEST 1
#define SQLITE_OMIT_CAST 1
#define SQLITE_OMIT_CHECK 1
#define SQLITE_OMIT_COMPILEOPTION_DIAGS 1
#define SQLITE_OMIT_COMPOUND_SELECT 1
#define SQLITE_OMIT_CTE 1
#define SQLITE_OMIT_DECLTYPE 1
#define SQLITE_OMIT_DEPRECATED 1
#define SQLITE_OMIT_EXPLAIN 1
#define SQLITE_OMIT_FLAG_PRAGMAS 1
#define SQLITE_OMIT_FOREIGN_KEY 1
#define SQLITE_OMIT_GET_TABLE 1
#define SQLITE_OMIT_INCRBLOB 1
#define SQLITE_OMIT_INTEGRITY_CHECK 1
#define SQLITE_OMIT_LIKE_OPTIMIZATION 1
#define SQLITE_OMIT_LOAD_EXTENSION 1
#define SQLITE_OMIT_LOCALTIME 1
#define SQLITE_OMIT_LOOKASIDE 1
#define SQLITE_OMIT_MEMORYDB 1
#define SQLITE_OMIT_OR_OPTIMIZATION 1
#define SQLITE_OMIT_PAGER_PRAGMAS 1
#define SQLITE_OMIT_PRAGMA 1
#define SQLITE_OMIT_PROGRESS_CALLBACK 1
#define SQLITE_OMIT_QUICKBALANCE 1
#define SQLITE_OMIT_REINDEX 1
#define SQLITE_OMIT_SCHEMA_PRAGMAS 1
#define SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS 1
#define SQLITE_OMIT_SHARED_CACHE 1
#define SQLITE_OMIT_TCL_VARIABLE 1
#define SQLITE_OMIT_TEMPDB 1
#define SQLITE_OMIT_TRACE 1
#define SQLITE_OMIT_TRIGGER 1
#define SQLITE_OMIT_TRUNCATE_OPTIMIZATION 1
#define SQLITE_OMIT_UTF16 1
#define SQLITE_OMIT_VIEW 1
#define SQLITE_OMIT_VIRTUALTABLE 1
#define SQLITE_OMIT_WAL 1
#define SQLITE_OMIT_XFER_OPT 1
/* #define SQLITE_OMIT_COMPLETE 1 */
/* #define SQLITE_OMIT_SUBQUERY 1 */
/* #define SQLITE_OMIT_DATETIME_FUNCS 1 */
/* #define SQLITE_OMIT_FLOATING_POINT 1 */
/*
/* From: https://chromium.googlesource.com/chromium/src.git/+/4.1.249.1050/third_party/sqlite/src/os_symbian.cc
* https://github.com/spsoft/spmemvfs/tree/master/spmemvfs
* http://www.sqlite.org/src/doc/trunk/src/test_demovfs.c
* http://www.sqlite.org/src/doc/trunk/src/test_vfstrace.c
* http://www.sqlite.org/src/doc/trunk/src/test_onefile.c
* http://www.sqlite.org/src/doc/trunk/src/test_vfs.c
**/
#include <c_stdio.h>
#include <c_stdlib.h>
#include <c_string.h>
#include <c_types.h>
#include <osapi.h>
#include <vfs.h>
#include <time.h>
#include <spi_flash.h>
#include <sqlite3.h>
#undef dbg_printf
#define dbg_printf(...) 0
#define CACHEBLOCKSZ 64
#define ESP8266_DEFAULT_MAXNAMESIZE 32
static int esp8266_Close(sqlite3_file*);
static int esp8266_Lock(sqlite3_file *, int);
static int esp8266_Unlock(sqlite3_file*, int);
static int esp8266_Sync(sqlite3_file*, int);
static int esp8266_Open(sqlite3_vfs*, const char *, sqlite3_file *, int, int*);
static int esp8266_Read(sqlite3_file*, void*, int, sqlite3_int64);
static int esp8266_Write(sqlite3_file*, const void*, int, sqlite3_int64);
static int esp8266_Truncate(sqlite3_file*, sqlite3_int64);
static int esp8266_Delete(sqlite3_vfs*, const char *, int);
static int esp8266_FileSize(sqlite3_file*, sqlite3_int64*);
static int esp8266_Access(sqlite3_vfs*, const char*, int, int*);
static int esp8266_FullPathname( sqlite3_vfs*, const char *, int, char*);
static int esp8266_CheckReservedLock(sqlite3_file*, int *);
static int esp8266_FileControl(sqlite3_file *, int, void*);
static int esp8266_SectorSize(sqlite3_file*);
static int esp8266_DeviceCharacteristics(sqlite3_file*);
static void* esp8266_DlOpen(sqlite3_vfs*, const char *);
static void esp8266_DlError(sqlite3_vfs*, int, char*);
static void (*esp8266_DlSym (sqlite3_vfs*, void*, const char*))(void);
static void esp8266_DlClose(sqlite3_vfs*, void*);
static int esp8266_Randomness(sqlite3_vfs*, int, char*);
static int esp8266_Sleep(sqlite3_vfs*, int);
static int esp8266_CurrentTime(sqlite3_vfs*, double*);
static int esp8266mem_Close(sqlite3_file*);
static int esp8266mem_Read(sqlite3_file*, void*, int, sqlite3_int64);
static int esp8266mem_Write(sqlite3_file*, const void*, int, sqlite3_int64);
static int esp8266mem_FileSize(sqlite3_file*, sqlite3_int64*);
static int esp8266mem_Sync(sqlite3_file*, int);
typedef struct st_linkedlist {
uint16_t blockid;
struct st_linkedlist *next;
uint8_t data[CACHEBLOCKSZ];
} linkedlist_t, *pLinkedList_t;
typedef struct st_filecache {
uint32_t size;
linkedlist_t *list;
} filecache_t, *pFileCache_t;
typedef struct esp8266_file {
sqlite3_file base;
int fd;
filecache_t *cache;
char name[ESP8266_DEFAULT_MAXNAMESIZE];
} esp8266_file;
static sqlite3_vfs esp8266Vfs = {
1, // iVersion
sizeof(esp8266_file), // szOsFile
FS_OBJ_NAME_LEN, // mxPathname
NULL, // pNext
"esp8266", // name
0, // pAppData
esp8266_Open, // xOpen
esp8266_Delete, // xDelete
esp8266_Access, // xAccess
esp8266_FullPathname, // xFullPathname
esp8266_DlOpen, // xDlOpen
esp8266_DlError, // xDlError
esp8266_DlSym, // xDlSym
esp8266_DlClose, // xDlClose
esp8266_Randomness, // xRandomness
esp8266_Sleep, // xSleep
esp8266_CurrentTime, // xCurrentTime
0 // xGetLastError
};
static const sqlite3_io_methods esp8266IoMethods = {
1,
esp8266_Close,
esp8266_Read,
esp8266_Write,
esp8266_Truncate,
esp8266_Sync,
esp8266_FileSize,
esp8266_Lock,
esp8266_Unlock,
esp8266_CheckReservedLock,
esp8266_FileControl,
esp8266_SectorSize,
esp8266_DeviceCharacteristics
};
static const sqlite3_io_methods esp8266MemMethods = {
1,
esp8266mem_Close,
esp8266mem_Read,
esp8266mem_Write,
esp8266_Truncate,
esp8266mem_Sync,
esp8266mem_FileSize,
esp8266_Lock,
esp8266_Unlock,
esp8266_CheckReservedLock,
esp8266_FileControl,
esp8266_SectorSize,
esp8266_DeviceCharacteristics
};
static uint32_t linkedlist_store (linkedlist_t **leaf, uint32_t offset, uint32_t len, const uint8_t *data) {
const uint8_t blank[CACHEBLOCKSZ] = { 0 };
uint16_t blockid = offset/CACHEBLOCKSZ;
linkedlist_t *block;
if (!memcmp(data, blank, CACHEBLOCKSZ))
return len;
block = *leaf;
if (!block || ( block->blockid != blockid ) ) {
block = sqlite3_malloc ( sizeof( linkedlist_t ) );
if (!block)
return SQLITE_NOMEM;
memset (block->data, 0, CACHEBLOCKSZ);
block->blockid = blockid;
}
if (!*leaf) {
*leaf = block;
block->next = NULL;
} else if (block != *leaf) {
if (block->blockid > (*leaf)->blockid) {
block->next = (*leaf)->next;
(*leaf)->next = block;
} else {
block->next = (*leaf);
(*leaf) = block;
}
}
memcpy (block->data + offset%CACHEBLOCKSZ, data, len);
return len;
}
static uint32_t filecache_pull (pFileCache_t cache, uint32_t offset, uint32_t len, uint8_t *data) {
uint16_t i;
float blocks;
uint32_t r = 0;
blocks = ( offset % CACHEBLOCKSZ + len ) / (float) CACHEBLOCKSZ;
if (blocks == 0.0)
return 0;
if (( blocks - (int) blocks) > 0.0)
blocks = blocks + 1.0;
for (i = 0; i < (uint16_t) blocks; i++) {
uint16_t round;
float relablock;
linkedlist_t *leaf;
uint32_t relaoffset, relalen;
uint8_t * reladata = (uint8_t*) data;
relalen = len - r;
reladata = reladata + r;
relaoffset = offset + r;
round = CACHEBLOCKSZ - relaoffset%CACHEBLOCKSZ;
if (relalen > round) relalen = round;
for (leaf = cache->list; leaf && leaf->next; leaf = leaf->next) {
if ( ( leaf->next->blockid * CACHEBLOCKSZ ) > relaoffset )
break;
}
relablock = relaoffset/((float)CACHEBLOCKSZ) - leaf->blockid;
if ( ( relablock >= 0 ) && ( relablock < 1 ) )
memcpy (data + r, leaf->data + (relaoffset % CACHEBLOCKSZ), relalen);
r = r + relalen;
}
return 0;
}
static uint32_t filecache_push (pFileCache_t cache, uint32_t offset, uint32_t len, const uint8_t *data) {
uint16_t i;
float blocks;
uint32_t r = 0;
uint8_t updateroot = 0x1;
blocks = ( offset % CACHEBLOCKSZ + len ) / (float) CACHEBLOCKSZ;
if (blocks == 0.0)
return 0;
if (( blocks - (int) blocks) > 0.0)
blocks = blocks + 1.0;
for (i = 0; i < (uint16_t) blocks; i++) {
uint16_t round;
uint32_t localr;
linkedlist_t *leaf;
uint32_t relaoffset, relalen;
uint8_t * reladata = (uint8_t*) data;
relalen = len - r;
reladata = reladata + r;
relaoffset = offset + r;
round = CACHEBLOCKSZ - relaoffset%CACHEBLOCKSZ;
if (relalen > round) relalen = round;
for (leaf = cache->list; leaf && leaf->next; leaf = leaf->next) {
if ( ( leaf->next->blockid * CACHEBLOCKSZ ) > relaoffset )
break;
updateroot = 0x0;
}
localr = linkedlist_store(&leaf, relaoffset, (relalen > CACHEBLOCKSZ) ? CACHEBLOCKSZ : relalen, reladata);
if (localr == SQLITE_NOMEM)
return SQLITE_NOMEM;
r = r + localr;
if (updateroot & 0x1)
cache->list = leaf;
}
if (offset + len > cache->size)
cache->size = offset + len;
return r;
}
static void filecache_free (pFileCache_t cache) {
pLinkedList_t this = cache->list, next;
while (this != NULL) {
next = this->next;
sqlite3_free (this);
this = next;
}
}
static int esp8266mem_Close(sqlite3_file *id)
{
esp8266_file *file = (esp8266_file*) id;
filecache_free(file->cache);
sqlite3_free (file->cache);
dbg_printf("esp8266mem_Close: %s OK\n", file->name);
return SQLITE_OK;
}
static int esp8266mem_Read(sqlite3_file *id, void *buffer, int amount, sqlite3_int64 offset)
{
sint32_t ofst;
esp8266_file *file = (esp8266_file*) id;
ofst = (sint32_t)(offset & 0x7FFFFFFF);
filecache_pull (file->cache, ofst, amount, buffer);
dbg_printf("esp8266mem_Read: %s [%ld] [%d] OK\n", file->name, ofst, amount);
return SQLITE_OK;
}
static int esp8266mem_Write(sqlite3_file *id, const void *buffer, int amount, sqlite3_int64 offset)
{
sint32_t ofst;
esp8266_file *file = (esp8266_file*) id;
ofst = (sint32_t)(offset & 0x7FFFFFFF);
filecache_push (file->cache, ofst, amount, buffer);
dbg_printf("esp8266mem_Write: %s [%ld] [%d] OK\n", file->name, ofst, amount);
return SQLITE_OK;
}
static int esp8266mem_Sync(sqlite3_file *id, int flags)
{
esp8266_file *file = (esp8266_file*) id;
dbg_printf("esp8266mem_Sync: %s OK\n", file->name);
return SQLITE_OK;
}
static int esp8266mem_FileSize(sqlite3_file *id, sqlite3_int64 *size)
{
esp8266_file *file = (esp8266_file*) id;
*size = 0LL | file->cache->size;
dbg_printf("esp8266mem_FileSize: %s [%d] OK\n", file->name, file->cache->size);
return SQLITE_OK;
}
static int esp8266_Open( sqlite3_vfs * vfs, const char * path, sqlite3_file * file, int flags, int * outflags )
{
int rc;
char *mode = "r";
esp8266_file *p = (esp8266_file*) file;
if ( path == NULL ) return SQLITE_IOERR;
if( flags&SQLITE_OPEN_READONLY ) mode = "r";
if( flags&SQLITE_OPEN_READWRITE || flags&SQLITE_OPEN_MAIN_JOURNAL ) {
int result;
if (SQLITE_OK != esp8266_Access(vfs, path, flags, &result))
return SQLITE_CANTOPEN;
if (result == 1)
mode = "r+";
else
mode = "w+";
}
dbg_printf("esp8266_Open: 1o %s %s\n", path, mode);
memset (p, 0, sizeof(esp8266_file));
strncpy (p->name, path, ESP8266_DEFAULT_MAXNAMESIZE);
p->name[ESP8266_DEFAULT_MAXNAMESIZE-1] = '\0';
if( flags&SQLITE_OPEN_MAIN_JOURNAL ) {
p->fd = 0;
p->cache = sqlite3_malloc(sizeof (filecache_t));
if (! p->cache )
return SQLITE_NOMEM;
memset (p->cache, 0, sizeof(filecache_t));
p->base.pMethods = &esp8266MemMethods;
dbg_printf("esp8266_Open: 2o %s %d MEM OK\n", p->name, p->fd);
return SQLITE_OK;
}
p->fd = vfs_open (path, mode);
if ( p->fd <= 0 ) {
return SQLITE_CANTOPEN;
}
p->base.pMethods = &esp8266IoMethods;
dbg_printf("esp8266_Open: 2o %s %d OK\n", p->name, p->fd);
return SQLITE_OK;
}
static int esp8266_Close(sqlite3_file *id)
{
esp8266_file *file = (esp8266_file*) id;
int rc = vfs_close(file->fd);
dbg_printf("esp8266_Close: %s %d %d\n", file->name, file->fd, rc);
return rc ? SQLITE_IOERR_CLOSE : SQLITE_OK;
}
static int esp8266_Read(sqlite3_file *id, void *buffer, int amount, sqlite3_int64 offset)
{
size_t nRead;
sint32_t ofst, iofst;
esp8266_file *file = (esp8266_file*) id;
iofst = (sint32_t)(offset & 0x7FFFFFFF);
dbg_printf("esp8266_Read: 1r %s %d %d %lld[%ld] \n", file->name, file->fd, amount, offset, iofst);
ofst = vfs_lseek(file->fd, iofst, VFS_SEEK_SET);
if (ofst != iofst) {
dbg_printf("esp8266_Read: 2r %ld != %ld FAIL\n", ofst, iofst);
return SQLITE_IOERR_SHORT_READ /* SQLITE_IOERR_SEEK */;
}
nRead = vfs_read(file->fd, buffer, amount);
if ( nRead == amount ) {
dbg_printf("esp8266_Read: 3r %s %u %d OK\n", file->name, nRead, amount);
return SQLITE_OK;
} else if ( nRead >= 0 ) {
dbg_printf("esp8266_Read: 3r %s %u %d FAIL\n", file->name, nRead, amount);
return SQLITE_IOERR_SHORT_READ;
}
dbg_printf("esp8266_Read: 4r %s FAIL\n", file->name);
return SQLITE_IOERR_READ;
}
static int esp8266_Write(sqlite3_file *id, const void *buffer, int amount, sqlite3_int64 offset)
{
size_t nWrite;
sint32_t ofst, iofst;
esp8266_file *file = (esp8266_file*) id;
iofst = (sint32_t)(offset & 0x7FFFFFFF);
dbg_printf("esp8266_Write: 1w %s %d %d %lld[%ld] \n", file->name, file->fd, amount, offset, iofst);
ofst = vfs_lseek(file->fd, iofst, VFS_SEEK_SET);
if (ofst != iofst) {
return SQLITE_IOERR_SEEK;
}
nWrite = vfs_write(file->fd, buffer, amount);
if ( nWrite != amount ) {
dbg_printf("esp8266_Write: 2w %s %u %d\n", file->name, nWrite, amount);
return SQLITE_IOERR_WRITE;
}
dbg_printf("esp8266_Write: 3w %s OK\n", file->name);
return SQLITE_OK;
}
static int esp8266_Truncate(sqlite3_file *id, sqlite3_int64 bytes)
{
esp8266_file *file = (esp8266_file*) id;
dbg_printf("esp8266_Truncate:\n");
return 0 ? SQLITE_IOERR_TRUNCATE : SQLITE_OK;
}
static int esp8266_Delete( sqlite3_vfs * vfs, const char * path, int syncDir )
{
sint32_t rc = vfs_remove( path );
if (rc == VFS_RES_ERR)
return SQLITE_IOERR_DELETE;
dbg_printf("esp8266_Delete: %s OK\n", path);
return SQLITE_OK;
}
static int esp8266_FileSize(sqlite3_file *id, sqlite3_int64 *size)
{
esp8266_file *file = (esp8266_file*) id;
*size = 0LL | vfs_size( file->fd );
dbg_printf("esp8266_FileSize: %s %u[%lld]\n", file->name, vfs_size(file->fd), *size);
return SQLITE_OK;
}
static int esp8266_Sync(sqlite3_file *id, int flags)
{
esp8266_file *file = (esp8266_file*) id;
int rc = vfs_flush( file->fd );
dbg_printf("esp8266_Sync: %d\n", rc);
return rc ? SQLITE_IOERR_FSYNC : SQLITE_OK;
}
static int esp8266_Access( sqlite3_vfs * vfs, const char * path, int flags, int * result )
{
struct vfs_stat st;
sint32_t rc = vfs_stat( path, &st );
*result = ( rc != VFS_RES_ERR );
dbg_printf("esp8266_Access: %d\n", *result);
return SQLITE_OK;
}
static int esp8266_FullPathname( sqlite3_vfs * vfs, const char * path, int len, char * fullpath )
{
struct vfs_stat st;
sint32_t rc = vfs_stat( path, &st );
if ( rc == VFS_RES_OK ){
strncpy( fullpath, st.name, len );
} else {
strncpy( fullpath, path, len );
}
fullpath[ len - 1 ] = '\0';
dbg_printf("esp8266_FullPathname: %s\n", fullpath);
return SQLITE_OK;
}
static int esp8266_Lock(sqlite3_file *id, int lock_type)
{
esp8266_file *file = (esp8266_file*) id;
dbg_printf("esp8266_Lock:\n");
return SQLITE_OK;
}
static int esp8266_Unlock(sqlite3_file *id, int lock_type)
{
esp8266_file *file = (esp8266_file*) id;
dbg_printf("esp8266_Unlock:\n");
return SQLITE_OK;
}
static int esp8266_CheckReservedLock(sqlite3_file *id, int *result)
{
esp8266_file *file = (esp8266_file*) id;
*result = 0;
dbg_printf("esp8266_CheckReservedLock:\n");
return SQLITE_OK;
}
static int esp8266_FileControl(sqlite3_file *id, int op, void *arg)
{
esp8266_file *file = (esp8266_file*) id;
dbg_printf("esp8266_FileControl:\n");
return SQLITE_OK;
}
static int esp8266_SectorSize(sqlite3_file *id)
{
esp8266_file *file = (esp8266_file*) id;
dbg_printf("esp8266_SectorSize:\n");
return SPI_FLASH_SEC_SIZE;
}
static int esp8266_DeviceCharacteristics(sqlite3_file *id)
{
esp8266_file *file = (esp8266_file*) id;
dbg_printf("esp8266_DeviceCharacteristics:\n");
return 0;
}
static void * esp8266_DlOpen( sqlite3_vfs * vfs, const char * path )
{
dbg_printf("esp8266_DlOpen:\n");
return NULL;
}
static void esp8266_DlError( sqlite3_vfs * vfs, int len, char * errmsg )
{
dbg_printf("esp8266_DlError:\n");
return;
}
static void ( * esp8266_DlSym ( sqlite3_vfs * vfs, void * handle, const char * symbol ) ) ( void )
{
dbg_printf("esp8266_DlSym:\n");
return NULL;
}
static void esp8266_DlClose( sqlite3_vfs * vfs, void * handle )
{
dbg_printf("esp8266_DlClose:\n");
return;
}
static int esp8266_Randomness( sqlite3_vfs * vfs, int len, char * buffer )
{
int rc = os_get_random(buffer, len);
dbg_printf("esp8266_Randomness: %d\n", rc);
return SQLITE_OK;
}
static int esp8266_Sleep( sqlite3_vfs * vfs, int microseconds )
{
dbg_printf("esp8266_Sleep:\n");
return SQLITE_OK;
}
static int esp8266_CurrentTime( sqlite3_vfs * vfs, double * result )
{
// This is stubbed out until we have a working RTCTIME solution;
// as it stood, this would always have returned the UNIX epoch.
// time_t t = time(NULL);
// *result = t / 86400.0 + 2440587.5;
*result = 2440587.5;
dbg_printf("esp8266_CurrentTime: %g\n", *result);
return SQLITE_OK;
}
int sqlite3_os_init(void){
sqlite3_vfs_register(&esp8266Vfs, 1);
return SQLITE_OK;
}
int sqlite3_os_end(void){
return SQLITE_OK;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef SQLITE3EXT_H
#define SQLITE3EXT_H
#include "sqlite3.h"
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*snprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
/* Version 3.7.16 and later */
int (*close_v2)(sqlite3*);
const char *(*db_filename)(sqlite3*,const char*);
int (*db_readonly)(sqlite3*,const char*);
int (*db_release_memory)(sqlite3*);
const char *(*errstr)(int);
int (*stmt_busy)(sqlite3_stmt*);
int (*stmt_readonly)(sqlite3_stmt*);
int (*stricmp)(const char*,const char*);
int (*uri_boolean)(const char*,const char*,int);
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
const char *(*uri_parameter)(const char*,const char*);
char *(*vsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
void(*)(void*));
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
void(*)(void*),unsigned char);
int (*cancel_auto_extension)(void(*)(void));
int (*load_extension)(sqlite3*,const char*,const char*,char**);
void *(*malloc64)(sqlite3_uint64);
sqlite3_uint64 (*msize)(void*);
void *(*realloc64)(void*,sqlite3_uint64);
void (*reset_auto_extension)(void);
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
void(*)(void*));
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
/* Version 3.8.11 and later */
sqlite3_value *(*value_dup)(const sqlite3_value*);
void (*value_free)(sqlite3_value*);
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
/* Version 3.9.0 and later */
unsigned int (*value_subtype)(sqlite3_value*);
void (*result_subtype)(sqlite3_context*,unsigned int);
/* Version 3.10.0 and later */
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
int (*strlike)(const char*,const char*,unsigned int);
int (*db_cacheflush)(sqlite3*);
/* Version 3.12.0 and later */
int (*system_errno)(sqlite3*);
/* Version 3.14.0 and later */
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
char *(*expanded_sql)(sqlite3_stmt*);
/* Version 3.18.0 and later */
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
};
/*
** This is the function signature used for all extension entry points. It
** is also defined in the file "loadext.c".
*/
typedef int (*sqlite3_loadext_entry)(
sqlite3 *db, /* Handle to the database. */
char **pzErrMsg, /* Used to set error string on failure. */
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
);
/*
** The following macros redefine the API routines so that they are
** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->snprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
/* Version 3.7.16 and later */
#define sqlite3_close_v2 sqlite3_api->close_v2
#define sqlite3_db_filename sqlite3_api->db_filename
#define sqlite3_db_readonly sqlite3_api->db_readonly
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
#define sqlite3_errstr sqlite3_api->errstr
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
#define sqlite3_stricmp sqlite3_api->stricmp
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
#define sqlite3_uri_int64 sqlite3_api->uri_int64
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
#define sqlite3_bind_text64 sqlite3_api->bind_text64
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
#define sqlite3_load_extension sqlite3_api->load_extension
#define sqlite3_malloc64 sqlite3_api->malloc64
#define sqlite3_msize sqlite3_api->msize
#define sqlite3_realloc64 sqlite3_api->realloc64
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
/* Version 3.8.11 and later */
#define sqlite3_value_dup sqlite3_api->value_dup
#define sqlite3_value_free sqlite3_api->value_free
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
/* Version 3.9.0 and later */
#define sqlite3_value_subtype sqlite3_api->value_subtype
#define sqlite3_result_subtype sqlite3_api->result_subtype
/* Version 3.10.0 and later */
#define sqlite3_status64 sqlite3_api->status64
#define sqlite3_strlike sqlite3_api->strlike
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
/* Version 3.12.0 and later */
#define sqlite3_system_errno sqlite3_api->system_errno
/* Version 3.14.0 and later */
#define sqlite3_trace_v2 sqlite3_api->trace_v2
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
/* Version 3.18.0 and later */
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
# define SQLITE_EXTENSION_INIT3 \
extern const sqlite3_api_routines *sqlite3_api;
#else
/* This case when the file is being statically linked into the
** application */
# define SQLITE_EXTENSION_INIT1 /*no-op*/
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
# define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif
#endif /* SQLITE3EXT_H */
...@@ -76,12 +76,6 @@ static tsl2561Gain_t _tsl2561Gain = TSL2561_GAIN_1X; ...@@ -76,12 +76,6 @@ static tsl2561Gain_t _tsl2561Gain = TSL2561_GAIN_1X;
static tsl2561Address_t tsl2561Address = TSL2561_ADDRESS_FLOAT; static tsl2561Address_t tsl2561Address = TSL2561_ADDRESS_FLOAT;
static tsl2561Package_t tsl2561Package = TSL2561_PACKAGE_T_FN_CL; static tsl2561Package_t tsl2561Package = TSL2561_PACKAGE_T_FN_CL;
static void delay_ms(uint16_t ms)
{
while (ms--)
os_delay_us(1000);
}
/**************************************************************************/ /**************************************************************************/
/*! /*!
@brief Writes an 8 bit values over I2C @brief Writes an 8 bit values over I2C
...@@ -236,13 +230,13 @@ tsl2561Error_t tsl2561GetLuminosity(uint16_t *broadband, uint16_t *ir) { ...@@ -236,13 +230,13 @@ tsl2561Error_t tsl2561GetLuminosity(uint16_t *broadband, uint16_t *ir) {
// Wait x ms for ADC to complete // Wait x ms for ADC to complete
switch (_tsl2561IntegrationTime) { switch (_tsl2561IntegrationTime) {
case TSL2561_INTEGRATIONTIME_13MS: case TSL2561_INTEGRATIONTIME_13MS:
delay_ms(14); //systickDelay(14); os_delay_us(14000); //systickDelay(14);
break; break;
case TSL2561_INTEGRATIONTIME_101MS: case TSL2561_INTEGRATIONTIME_101MS:
delay_ms(102); //systickDelay(102); os_delay_us(102000); //systickDelay(102);
break; break;
default: default:
delay_ms(404); //systickDelay(404); os_delay_us(404000); //systickDelay(404);
break; break;
} }
......
...@@ -24,7 +24,7 @@ STD_CFLAGS=-std=gnu11 -Wimplicit ...@@ -24,7 +24,7 @@ STD_CFLAGS=-std=gnu11 -Wimplicit
# makefile at its root level - these are then overridden # makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein # for a subtree within the makefile rooted therein
# #
DEFINES += -DESP_INIT_DATA_DEFAULT="\"$(SDK_DIR)/bin/esp_init_data_default.bin\"" DEFINES += -DESP_INIT_DATA_DEFAULT="\"$(SDK_DIR)/bin/esp_init_data_default_v05.bin\""
############################################################# #############################################################
# Recursion Magic - Don't touch this!! # Recursion Magic - Don't touch this!!
......
...@@ -150,9 +150,13 @@ void nodemcu_init(void) ...@@ -150,9 +150,13 @@ void nodemcu_init(void)
return; return;
} }
#if 0
// espconn_secure_set_size() is not effective
// see comments for MBEDTLS_SSL_MAX_CONTENT_LEN in user_mbedtls.h
#if defined ( CLIENT_SSL_ENABLE ) && defined ( SSL_BUFFER_SIZE ) #if defined ( CLIENT_SSL_ENABLE ) && defined ( SSL_BUFFER_SIZE )
espconn_secure_set_size(ESPCONN_CLIENT, SSL_BUFFER_SIZE); espconn_secure_set_size(ESPCONN_CLIENT, SSL_BUFFER_SIZE);
#endif #endif
#endif
#ifdef BUILD_SPIFFS #ifdef BUILD_SPIFFS
if (!vfs_mount("/FLASH", 0)) { if (!vfs_mount("/FLASH", 0)) {
......
...@@ -6,3 +6,4 @@ ...@@ -6,3 +6,4 @@
!.gitignore !.gitignore
!blank.bin !blank.bin
!esp_init_data_default.bin !esp_init_data_default.bin
!esp_init_data_default_v05.bin
...@@ -75,7 +75,7 @@ make EXTRA_CCFLAGS="-DLUA_NUMBER_INTEGRAL .... ...@@ -75,7 +75,7 @@ make EXTRA_CCFLAGS="-DLUA_NUMBER_INTEGRAL ....
Identify your firmware builds by editing `app/include/user_version.h` Identify your firmware builds by editing `app/include/user_version.h`
```c ```c
#define NODE_VERSION "NodeMCU 2.1.0+myname" #define NODE_VERSION "NodeMCU " ESP_SDK_VERSION_STRING "." NODE_VERSION_XSTR(NODE_VERSION_INTERNAL)
#ifndef BUILD_DATE #ifndef BUILD_DATE
#define BUILD_DATE "YYYYMMDD" #define BUILD_DATE "YYYYMMDD"
#endif #endif
......
...@@ -24,7 +24,7 @@ Run the following command to flash an *aggregated* binary as is produced for exa ...@@ -24,7 +24,7 @@ Run the following command to flash an *aggregated* binary as is produced for exa
`esptool.py --port <serial-port-of-ESP8266> write_flash -fm <mode> 0x00000 <nodemcu-firmware>.bin` `esptool.py --port <serial-port-of-ESP8266> write_flash -fm <mode> 0x00000 <nodemcu-firmware>.bin`
`mode` is `qio` for 512&nbsp;kByte modules and `dio` for >=4&nbsp;MByte modules (`qio` might work as well, YMMV). [`mode`](https://github.com/espressif/esptool/#flash-modes) is `qio` for most ESP8266 ESP-01/07 (512&nbsp;kByte modules) and `dio` for most ESP32 and ESP8266 ESP-12 (>=4&nbsp;MByte modules). ESP8285 requires `dout`.
**Gotchas** **Gotchas**
...@@ -34,24 +34,25 @@ Run the following command to flash an *aggregated* binary as is produced for exa ...@@ -34,24 +34,25 @@ Run the following command to flash an *aggregated* binary as is produced for exa
- In some uncommon cases, the [SDK init data](#sdk-init-data) may be invalid and NodeMCU may fail to boot. The easiest solution is to fully erase the chip before flashing: - In some uncommon cases, the [SDK init data](#sdk-init-data) may be invalid and NodeMCU may fail to boot. The easiest solution is to fully erase the chip before flashing:
`esptool.py --port <serial-port-of-ESP8266> erase_flash` `esptool.py --port <serial-port-of-ESP8266> erase_flash`
### NodeMCU Flasher
> A firmware Flash tool for NodeMCU...We are working on next version and will use QT framework. It will be cross platform and open-source.
Source: [https://github.com/nodemcu/nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher)
Supported platforms: Windows
Note that this tool was created by the initial developers of the NodeMCU firmware. **It hasn't seen updates since September 2015** and is not maintained by the current NodeMCU *firmware* team. Be careful to not accidentally flash the very old default firmware the tool is shipped with.
### NodeMCU PyFlasher ### NodeMCU PyFlasher
> Self-contained [NodeMCU](https://github.com/nodemcu/nodemcu-firmware) flasher with GUI based on [esptool.py](https://github.com/espressif/esptool) and [wxPython](https://www.wxpython.org/). > Self-contained [NodeMCU](https://github.com/nodemcu/nodemcu-firmware) flasher with GUI based on [esptool.py](https://github.com/espressif/esptool) and [wxPython](https://www.wxpython.org/).
![NodeMCU PyFlasher](../img/NodeMCU-PyFlasher.png "NodeMCU PyFlasher")
Source: [https://github.com/marcelstoer/nodemcu-pyflasher](https://github.com/marcelstoer/nodemcu-pyflasher) Source: [https://github.com/marcelstoer/nodemcu-pyflasher](https://github.com/marcelstoer/nodemcu-pyflasher)
Supported platforms: anything that runs Python, runnable .exe available for Windows Supported platforms: anything that runs Python, runnable .exe available for Windows and .dmg for macOS
Disclaimer: the availability of [NodeMCU PyFlasher was announced on the NodeMCU Facebook page](https://www.facebook.com/NodeMCU/posts/663197460515251) but it is not an official offering of the current NodeMCU firmware team. Disclaimer: the availability of [NodeMCU PyFlasher was announced on the NodeMCU Facebook page](https://www.facebook.com/NodeMCU/posts/663197460515251) but it is not an official offering of the current NodeMCU firmware team.
### NodeMCU Flasher
> A firmware Flash tool for NodeMCU...We are working on next version and will use QT framework. It will be cross platform and open-source.
Source: [https://github.com/nodemcu/nodemcu-flasher](https://github.com/nodemcu/nodemcu-flasher)
Supported platforms: Windows
Note that this tool was created by the initial developers of the NodeMCU firmware. **It hasn't seen updates since September 2015** and is not maintained by the current NodeMCU *firmware* team. Be careful to not accidentally flash the very old default firmware the tool is shipped with.
## Putting Device Into Flash Mode ## Putting Device Into Flash Mode
...@@ -101,7 +102,7 @@ Espressif refers to this area as "System Param" and it resides in the last four ...@@ -101,7 +102,7 @@ Espressif refers to this area as "System Param" and it resides in the last four
The default init data is provided as part of the SDK in the file `esp_init_data_default.bin`. NodeMCU will automatically flash this file to the right place on first boot if the sector appears to be empty. The default init data is provided as part of the SDK in the file `esp_init_data_default.bin`. NodeMCU will automatically flash this file to the right place on first boot if the sector appears to be empty.
If you need to customize init data then first download the [Espressif SDK 2.1.0](https://github.com/espressif/ESP8266_NONOS_SDK/archive/v2.1.0.zip) and extract `esp_init_data_default.bin`. Then flash that file just like you'd flash the firmware. The correct address for the init data depends on the capacity of the flash chip. If you need to customize init data then first download the [Espressif SDK 2.2.0](https://github.com/espressif/ESP8266_NONOS_SDK/archive/v2.2.0.zip) and extract `esp_init_data_default.bin`. Then flash that file just like you'd flash the firmware. The correct address for the init data depends on the capacity of the flash chip.
- `0x7c000` for 512 kB, modules like most ESP-01, -03, -07 etc. - `0x7c000` for 512 kB, modules like most ESP-01, -03, -07 etc.
- `0xfc000` for 1 MB, modules like ESP8285, PSF-A85, some ESP-01, -03 etc. - `0xfc000` for 1 MB, modules like ESP8285, PSF-A85, some ESP-01, -03 etc.
......
...@@ -30,14 +30,12 @@ Because the development is active this list will no doubt continue to be revised ...@@ -30,14 +30,12 @@ Because the development is active this list will no doubt continue to be revised
The NodeMCU firmware implements Lua 5.1 over the Espressif SDK for its ESP8266 SoC and the IoT modules based on this. The NodeMCU firmware implements Lua 5.1 over the Espressif SDK for its ESP8266 SoC and the IoT modules based on this.
* The official lua.org **[Lua Language specification](http://www.lua.org/manual/5.1/manual.html)** gives a terse but complete language specification. - The official lua.org **[Lua Language specification](http://www.lua.org/manual/5.1/manual.html)** gives a terse but complete language specification.
* Its [FAQ](http://www.lua.org/faq.html) provides information on Lua availability and licensing issues. - Its [FAQ](http://www.lua.org/faq.html) provides information on Lua availability and licensing issues.
* The **[unofficial Lua FAQ](http://www.luafaq.org/)** provides a lot of useful Q and A content, and is extremely useful for those learning Lua as a second language. - The **[unofficial Lua FAQ](http://www.luafaq.org/)** provides a lot of useful Q and A content, and is extremely useful for those learning Lua as a second language.
* The [Lua User's Wiki](http://lua-users.org/wiki/) gives useful example source and relevant discussion. In particular, its [Lua Learning Lua](http://lua-users.org/wiki/Learning) section is a good place to start learning Lua. - The [Lua User's Wiki](http://lua-users.org/wiki/) gives useful example source and relevant discussion. In particular, its [Lua Learning Lua](http://lua-users.org/wiki/Learning) section is a good place to start learning Lua.
* The best book to learn Lua is *Programming in Lua* by Roberto Ierusalimschy, one of the creators of Lua. It's first edition is available free [online](http://www.lua.org/pil/contents.html) . The second edition was aimed at Lua 5.1, but is out of print. The third edition is still in print and available in paperback. It contains a lot more material and clearly identifies Lua 5.1 vs Lua 5.2 differences. **This third edition is widely available for purchase and probably the best value for money**. References of the format [PiL **n.m**] refer to section **n.m** in this edition. - The best book to learn Lua is *Programming in Lua- by Roberto Ierusalimschy, one of the creators of Lua. It's first edition is available free [online](http://www.lua.org/pil/contents.html) . The second edition was aimed at Lua 5.1, but is out of print. The third edition is still in print and available in paperback. It contains a lot more material and clearly identifies Lua 5.1 vs Lua 5.2 differences. **This third edition is widely available for purchase and probably the best value for money**. References of the format [PiL **n.m**] refer to section **n.m** in this edition.
* The Espressif ESP8266 architecture is closed source, but the Espressif SDK itself is continually being updated so the best way to get the documentation for this is to [google Espressif IoT SDK Programming Guide](https://www.google.co.uk/search?q=Espressif+IoT+SDK+Programming+Guide) or to look at the Espressif [downloads forum](http://bbs.espressif.com/viewforum.php?f=5) . - The Espressif ESP8266 architecture is closed source, but the Espressif SDK itself is continually being updated so the best way to get the documentation for this is to [google Espressif IoT SDK Programming Guide](https://www.google.co.uk/search?q=Espressif+IoT+SDK+Programming+Guide) or to look at the Espressif [downloads forum](http://bbs.espressif.com/viewforum.php?f=5).
* The **NodeMCU documentation** is now available online, and this FAQ forms part of this.
* As with all Open Source projects the source for the NodeMCU firmware is openly available on the [GitHub NodeMCU-firmware](https://github.com/NodeMCU/NodeMCU-firmware) repository.
### How is NodeMCU Lua different to standard Lua? ### How is NodeMCU Lua different to standard Lua?
...@@ -51,68 +49,84 @@ NodeMCU Lua is based on [eLua](http://www.eluaproject.net/overview), a fully fea ...@@ -51,68 +49,84 @@ NodeMCU Lua is based on [eLua](http://www.eluaproject.net/overview), a fully fea
The main impacts of the ESP8266 SDK and together with its hardware resource limitations are not in the Lua language implementation itself, but in how *application programmers must approach developing and structuring their applications*. As discussed in detail below, the SDK is non-preemptive and event driven. Tasks can be associated with given events by using the SDK API to registering callback functions to the corresponding events. Events are queued internally within the SDK, and it then calls the associated tasks one at a time, with each task returning control to the SDK on completion. *The SDK states that if any tasks run for more than 15 mSec, then services such as WiFi can fail.* The main impacts of the ESP8266 SDK and together with its hardware resource limitations are not in the Lua language implementation itself, but in how *application programmers must approach developing and structuring their applications*. As discussed in detail below, the SDK is non-preemptive and event driven. Tasks can be associated with given events by using the SDK API to registering callback functions to the corresponding events. Events are queued internally within the SDK, and it then calls the associated tasks one at a time, with each task returning control to the SDK on completion. *The SDK states that if any tasks run for more than 15 mSec, then services such as WiFi can fail.*
The NodeMCU libraries act as C wrappers around registered Lua callback functions to enable these to be used as SDK tasks. ***You must therefore use an Event-driven programming style in writing your ESP8266 Lua programs***. Most programmers are used to writing in a procedural style where there is a clear single flow of execution, and the program interfaces to operating system services by a set of synchronous API calls to do network I/O, etc. Whilst the logic of each individual task is procedural, this is not how you code up ESP8266 applications. The NodeMCU libraries act as C wrappers around registered Lua callback functions to enable these to be used as SDK tasks. **You must therefore use an Event-driven programming style in writing your ESP8266 Lua programs**. Most programmers are used to writing in a procedural style where there is a clear single flow of execution, and the program interfaces to operating system services by a set of synchronous API calls to do network I/O, etc. Whilst the logic of each individual task is procedural, this is not how you code up ESP8266 applications.
## ESP8266 Specifics ## ESP8266 Specifics
### How is coding for the ESP8266 the same as standard Lua? ### How is coding for the ESP8266 the same as standard Lua?
* This is a fully featured Lua 5.1 implementation so all standard Lua language constructs and data types work. - This is a fully featured Lua 5.1 implementation so all standard Lua language constructs and data types work.
* The main standard Lua libraries -- `core`, `coroutine`, `string` and `table` are implemented. - The main standard Lua libraries -- `core`, `coroutine`, `string` and `table` are implemented.
### How is coding for the ESP8266 different to standard Lua? ### How is coding for the ESP8266 different to standard Lua?
* The ESP8266 uses a combination of on-chip RAM and off-chip Flash memory connected using a dedicated SPI interface. Code can be executed directly from Flash-mapped address space. In fact the ESP hardware actually executes code in RAM, and in the case of Flash-mapped addresses it executes this code from a RAM-based L1 cache which maps onto the Flash addresses. If the addressed line is in the cache then the code runs at full clock speed, but if not then the hardware transparently handles the adress fault by first copying the code from Flash to RAM. This is largely transparent in terms of programming ESP8266 applications, though the faulting access runs at SRAM speeds and this code runs perhaps 13× slower than already cached code. The Lua firmware largely runs out of Flash, but even so, both the RAM and the Flash memory are *very* limited when compared to systems that most application programmers use. The ESP8266 uses a combination of on-chip RAM and off-chip Flash memory connected using a dedicated SPI interface. Code can be executed directly from Flash-mapped address space. In fact the ESP hardware actually executes code in RAM, and in the case of Flash-mapped addresses it executes this code from a RAM-based L1 cache which maps onto the Flash addresses. If the addressed line is in the cache then the code runs at full clock speed, but if not then the hardware transparently handles the address fault by first copying the code from Flash to RAM. This is largely transparent in terms of programming ESP8266 applications, though the faulting access runs at SRAM speeds and this code runs perhaps 13× slower than already cached code. The Lua firmware largely runs out of Flash, but even so, both the RAM and the Flash memory are *very- limited when compared to systems that most application programmers use.
* Over the last two years, both the Espressif non-OS SDK developers and the NodeMCU team have made a range of improvements and optimisations to increase the amount of RAM available to developers, from a typical 15Kb or so with Version 0.9 builds to some 45Kb with the current firmware Version 2.x builds. See the [ESP8266 Non-OS SDK API Reference](https://espressif.com/sites/default/files/documentation/2c-esp8266_non_os_sdk_api_reference_en.pdf) for more detals on the SDK.
* The early ESP8266 modules were typically configured with 512Kb Flash. Fitting a fully featured Lua build with a number of optional libraries and still enough usable Flash to hold a Lua application was a struggle. However the code-size of the SDK has grown significantly between the early versions and the current 2.0 version. Applications based on the current SDK can no longer fit in 512Kb Flash memory, and so all currently produced ESP modules now contain a minimum of 1Mb with 4 and 16Mb becoming more common. The current NodeMCU firmware will fit comfortably in a 1Mb Flash and still have ample remaining Flash memory to support Lua IoT applications. Note that the [`1.5.4.1-final` branch](https://github.com/nodemcu/nodemcu-firmware/tree/1.5.4.1-final) is the last available release if you still wish to develop applications for 512Kb modules Over the last two years, both the Espressif non-OS SDK developers and the NodeMCU team have made a range of improvements and optimisations to increase the amount of RAM available to developers, from a typical 15Kb or so with Version 0.9 builds to some 45Kb with the current firmware Version 2.x builds. See the [ESP8266 Non-OS SDK API Reference](https://espressif.com/sites/default/files/documentation/2c-esp8266_non_os_sdk_api_reference_en.pdf) for more details on the SDK.
* The NodeMCU firmware makes any unused Flash memory available as a [SPI Flash File System (SPIFFS)](https://github.com/pellepl/spiffs) through the `file` library. The SPIFFS file system is designed for SPI NOR flash devices on embedded targets, and is optimised for static wear levelling and low RAM footprint. For further details, see the link. How much Flash is available as SPIFFS file space depends on the number of modules included in the specific firmware build.
* The firmware has a wide range of libraries available to support common hardware options. Including any library will increase both the code and RAM size of the build, so our recommended practice is for application developers to choose a custom build that only includes the library that are needed for your application and hardware variants. The developers that don't want to bother with setting up their own build environment can use Marcel Stör's excellent [Cloud build service](http://nodemcu-build.com) instead. The early ESP8266 modules were typically configured with 512Kb Flash. Fitting a fully featured Lua build with a number of optional libraries and still enough usable Flash to hold a Lua application needs a careful selection of libraries and features. The current NodeMCU firmware will fit comfortably in a 1Mb Flash and still have ample remaining Flash memory to support Lua IoT applications.
* There are also further tailoring options available, for example you can choose to have a firmware build which uses 32-bit integer arithmetic instead of floating point. Our integer builds have a smaller Flash footprint and execute faster, but working in integer also has a number of pitfalls, so our general recommendation is to use floating point builds.
* Unlike Arduino or ESP8266 development, where each application change requires the flashing of a new copy of the firmware, in the case of Lua the firmware is normally flashed once, and all application development is done by updating files on the SPIFFS file system. In this respect, Lua development on the ESP8266 is far more like developing applications on a more traditional PC. The firmware will only be reflashed if the developer wants to add or update one or more of the hardware-related libraries. The NodeMCU firmware makes any unused Flash memory available as a [SPI Flash File System (SPIFFS)](https://github.com/pellepl/spiffs) through the `file` library. The SPIFFS file system is designed for SPI NOR flash devices on embedded targets, and is optimised for static wear levelling and low RAM footprint. For further details, see the link. How much Flash is available as SPIFFS file space depends on the number of modules included in the specific firmware build.
* Those developers who are used to dealing in MB or GB of RAM and file systems can easily run out of memory resources, but with care and using some of the techniques discussed below can go a long way to mitigate this.
* The ESP8266 runs the SDK over the native hardware, so there is no underlying operating system to capture errors and to provide graceful failure modes. Hence system or application errors can easily "PANIC" the system causing it to reboot. Error handling has been kept simple to save on the limited code space, and this exacerbates this tendency. Running out of a system resource such as RAM will invariably cause a messy failure and system reboot. The firmware has a wide range of libraries available to support common hardware options. Including any library will increase both the code and RAM size of the build, so our recommended practice is for application developers to choose a custom build that only includes the library that are needed for your application and hardware variants. The developers that don't want to bother with setting up their own build environment can use Marcel Stör's excellent [Cloud build service](http://nodemcu-build.com) instead.
* Note that in the 3 years since the firmware was first developed, Espressif has developed and released a new RTOS alternative to the non-OS SDK, and and the latest version of the SDK API reference recommends using RTOS. Unfortunately, the richer RTOS has a significantly larger RAM footprint. Whilst our port to the ESP-32 (with its significantly larger RAM) uses the [ESP-IDF](https://github.com/espressif/esp-idf) which is based on RTOS, the ESP8266 RTOS versions don't have enough free RAM for a RTOS-based NodeMCU firmware build to have sufficient free RAM to write usable applications.
* There is currently no `debug` library support. So you have to use 1980s-style "binary-chop" to locate errors and use print statement diagnostics though the system's UART interface. (This omission was largely because of the Flash memory footprint of this library, but there is no reason in principle why we couldn't make this library available in the near future as a custom build option). There are also further tailoring options available, for example you can choose to have a firmware build which uses 32-bit integer arithmetic instead of floating point. Our integer builds have a smaller Flash footprint and execute faster, but working in integer also has a number of pitfalls, so our general recommendation is to use floating point builds.
* The LTR implementation means that you can't extend standard libraries as easily as you can in normal Lua, so for example an attempt to define `function table.pack()` will cause a runtime error because you can't write to the global `table`. Standard sand-boxing techniques can be used to achieve the same effect by using metatable based inheritance, but if you choose this option, then you need to be aware of the potential runtime and RAM impacts of this approach.
* There are standard libraries to provide access to the various hardware options supported by the hardware: WiFi, GPIO, One-wire, I²C, SPI, ADC, PWM, UART, etc. Unlike Arduino or ESP8266 development, where each application change requires the flashing of a new copy of the firmware, in the case of Lua the firmware is normally flashed once, and all application development is done by updating files on the SPIFFS file system. In this respect, Lua development on the ESP8266 is far more like developing applications on a more traditional PC. The firmware will only be reflashed if the developer wants to add or update one or more of the hardware-related libraries.
* The runtime system runs in interactive-mode. In this mode it first executes any `init.lua` script. It then "listens" to the serial port for input Lua chunks, and executes them once syntactically complete.
* There is no batch support, although automated embedded processing is normally achieved by setting up the necessary event triggers in the [`init.lua`](../upload/#initlua) script. Those developers who are used to dealing in MB or GB of RAM and file systems can easily run out of memory resources, but with care and using some of the techniques discussed below can go a long way to mitigate this.
* The various libraries (`net`, `tmr`, `wifi`, etc.) use the SDK callback mechanism to bind Lua processing to individual events (for example a timer alarm firing). Developers should make full use of these events to keep Lua execution sequences short.
* Non-Lua processing (e.g. network functions) will usually only take place once the current Lua chunk has completed execution. So any network calls should be viewed at an asynchronous request. A common coding mistake is to assume that they are synchronous, that is if two `socket:send()` are on consecutive lines in a Lua programme, then the first has completed by the time the second is executed. This is wrong. A `socket:send()` request simply queues the send task for dispatch by the SDK. This task can't start to process until the Lua code has returned to is calling C function to allow this running task to exit. Stacking up such requests in a single Lua task function burns scarce RAM and can trigger a PANIC. This is true for timer, network, and other callbacks. It is even the case for actions such as requesting a system restart, as can be seen by the following example which will print twenty "not quite yet" messages before restarting. The ESP8266 runs the SDK over the native hardware, so there is no underlying operating system to capture errors and to provide graceful failure modes. Hence system or application errors can easily "PANIC" the system causing it to reboot. Error handling has been kept simple to save on the limited code space, and this exacerbates this tendency. Running out of a system resource such as RAM will invariably cause a messy failure and system reboot.
Note that in the 3 years since the firmware was first developed, Espressif has developed and released a new RTOS alternative to the non-OS SDK, and and the latest version of the SDK API reference recommends using RTOS. Unfortunately, the richer RTOS has a significantly larger RAM footprint. Whilst our port to the ESP-32 (with its significantly larger RAM) uses the [ESP-IDF](https://github.com/espressif/esp-idf) which is based on RTOS, the ESP8266 RTOS versions don't have enough free RAM for a RTOS-based NodeMCU firmware build to have sufficient free RAM to write usable applications.
There is currently no `debug` library support. So you have to use 1980s-style "binary-chop" to locate errors and use print statement diagnostics though the system's UART interface. (This omission was largely because of the Flash memory footprint of this library, but there is no reason in principle why we couldn't make this library available in the near future as a custom build option).
The LTR implementation means that you can't extend standard libraries as easily as you can in normal Lua, so for example an attempt to define `function table.pack()` will cause a runtime error because you can't write to the global `table`. Standard sand-boxing techniques can be used to achieve the same effect by using metatable based inheritance, but if you choose this option, then you need to be aware of the potential runtime and RAM impacts of this approach.
There are standard libraries to provide access to the various hardware options supported by the hardware: WiFi, GPIO, One-wire, I²C, SPI, ADC, PWM, UART, etc.
The runtime system runs in interactive-mode. In this mode it first executes any `init.lua` script. It then "listens" to the serial port for input Lua chunks, and executes them once syntactically complete.
There is no batch support, although automated embedded processing is normally achieved by setting up the necessary event triggers in the [`init.lua`](../upload/#initlua) script.
The various libraries (`net`, `tmr`, `wifi`, etc.) use the SDK callback mechanism to bind Lua processing to individual events (for example a timer alarm firing). Developers should make full use of these events to keep Lua execution sequences short.
Non-Lua processing (e.g. network functions) will usually only take place once the current Lua chunk has completed execution. So any network calls should be viewed at an asynchronous request. A common coding mistake is to assume that they are synchronous, that is if two `socket:send()` are on consecutive lines in a Lua programme, then the first has completed by the time the second is executed. This is wrong. A `socket:send()` request simply queues the send task for dispatch by the SDK. This task can't start to process until the Lua code has returned to is calling C function to allow this running task to exit. Stacking up such requests in a single Lua task function burns scarce RAM and can trigger a PANIC. This is true for timer, network, and other callbacks. It is even the case for actions such as requesting a system restart, as can be seen by the following example which will print twenty "not quite yet" messages before restarting.
```lua ```lua
node.restart(); for i = 1, 20 do print("not quite yet -- ",i); end node.restart(); for i = 1, 20 do print("not quite yet -- ",i); end
``` ```
* You therefore *have* to implement ESP8266 Lua applications using an event driven approach. You have to understand which SDK API requests schedule asynchronous processing, and which define event actions through Lua callbacks. Yes, such an event-driven approach makes it difficult to develop procedurally structured applications, but it is well suited to developing the sorts of application that you will typically want to implement on an IoT device. You, therefore, **have to** implement ESP8266 Lua applications using an event driven approach. You have to understand which SDK API requests schedule asynchronous processing, and which define event actions through Lua callbacks. Yes, such an event-driven approach makes it difficult to develop procedurally structured applications, but it is well suited to developing the sorts of application that you will typically want to implement on an IoT device.
### So how does the SDK event / tasking system work in Lua? ### So how does the SDK event / tasking system work in Lua?
* The SDK uses a small number of Interrupt Service Routines (ISRs) to handle short time critical hardware interrupt related processing. These are very short duration and can interrupt a running task for up to 10µSec. (Modifying these ISRs or adding new ones is not a viable options for most developers.) - The SDK uses a small number of Interrupt Service Routines (ISRs) to handle short time critical hardware interrupt related processing. These are very short duration and can interrupt a running task for up to 10µSec. (Modifying these ISRs or adding new ones is not a viable options for most developers.)
* All other service and application processing is split into code execution blocks, known as **tasks**. The individual tasks are executed one at a time and run to completion. No task can never pre-empt another. - All other service and application processing is split into code execution blocks, known as **tasks**. The individual tasks are executed one at a time and run to completion. No task can never pre-empt another.
* Runnable tasks are queued in one of three priority queues and the SDK contains a simple scheduler which executes queued tasks FIFO within priority. The high priority queue is used for hardware-related task, the middle for timer and event-driven tasks and the low priority queue for all other tasks. - Runnable tasks are queued in one of three priority queues and the SDK contains a simple scheduler which executes queued tasks FIFO within priority. The high priority queue is used for hardware-related task, the middle for timer and event-driven tasks and the low priority queue for all other tasks.
* It is important to keep task times as short as practical so that the overall system can work smoothly and responsively. The general recommendation is to keep medium priority tasks under 2mSec and low priority tasks under 15 mSec in duration. This is a guideline, and your application *might* work stably if you exceed this, but you might also start to experience intermittent problems because of internal timeout within the WiFi and network services, etc.. - It is important to keep task times as short as practical so that the overall system can work smoothly and responsively. The general recommendation is to keep medium priority tasks under 2mSec and low priority tasks under 15 mSec in duration. This is a guideline, and your application _might_ work stably if you exceed this, but you might also start to experience intermittent problems because of internal timeout within the WiFi and network services, etc..
* If tasks take longer than 500mSec then the watchdog timer will reset the processor. This watchdog can be reset at an application level using the [`tmr.wdclr()`](modules/tmr/#tmrwdclr) function, but this should be avoided. - If tasks take longer than 500mSec then the watchdog timer will reset the processor. This watchdog can be reset at an application level using the [`tmr.wdclr()`](modules/tmr/#tmrwdclr) function, but this should be avoided.
* Application tasks can disable interrupts to prevent an ISR interrupting a time-critical code section, The SDK guideline is that system ISRs might overrun if such critical code section last more than 10µSec. This means that such disabling can only be done within hardware-related library modules, written in C; it is not available at a Lua application level. - Application tasks can disable interrupts to prevent an ISR interrupting a time-critical code section, The SDK guideline is that system ISRs might overrun if such critical code section last more than 10µSec. This means that such disabling can only be done within hardware-related library modules, written in C; it is not available at a Lua application level.
* The SDK provide a C API for interfacing to it; this includes a set of functions for declaring application functions (written in C) as callbacks to associate application tasks with specific hardware and timer events, and their execution will be interleaved with the SDKs Wifi and Network processing tasks. - The SDK provide a C API for interfacing to it; this includes a set of functions for declaring application functions (written in C) as callbacks to associate application tasks with specific hardware and timer events, and their execution will be interleaved with the SDKs Wifi and Network processing tasks.
In essence, the NodeMCU firmware is a C application which exploits the ability of Lua to execute as a embedded language and runtime to mirror this structure at a Lua scripting level. All of the complexities of, and interface to, the SDK and the hardware are wrapped in firmware libraries which translate the appropriate calls into the corresponding Lua API. In essence, the NodeMCU firmware is a C application which exploits the ability of Lua to execute as a embedded language and runtime to mirror this structure at a Lua scripting level. All of the complexities of, and interface to, the SDK and the hardware are wrapped in firmware libraries which translate the appropriate calls into the corresponding Lua API.
* The SDK invokes a startup hook within the firmware on boot-up. This firmware code initialises the Lua environment and then attempts to execute the Lua module `init.lua` from the SPIFFS file system. This `init.lua` module can then be used to do any application initialisation required and to call the necessary timer alarms or library calls to bind and callback routines to implement the tasks needed in response to any system events. - The SDK invokes a startup hook within the firmware on boot-up. This firmware code initialises the Lua environment and then attempts to execute the Lua module `init.lua` from the SPIFFS file system. This `init.lua` module can then be used to do any application initialisation required and to call the necessary timer alarms or library calls to bind and callback routines to implement the tasks needed in response to any system events.
* By default, the Lua runtime also 'listens' to UART 0, the serial port, in interactive mode and will execute any Lua commands input through this serial port. Using the serial port in this way is the most common method of developing and debugging Lua applications on the ESP8266/ - By default, the Lua runtime also 'listens' to UART 0, the serial port, in interactive mode and will execute any Lua commands input through this serial port. Using the serial port in this way is the most common method of developing and debugging Lua applications on the ESP8266/
* The Lua libraries provide a set of functions for declaring application functions (written in Lua) as callbacks (which are stored in the [Lua registry](#so-how-is-the-lua-registry-used-and-why-is-this-important)) to associate application tasks with specific hardware and timer events. These are also non-preemptive at an applications level. The Lua libraries work in consort with the SDK to queue pending events and invoke any registered Lua callback routines, which then run to completion uninterrupted. For example the Lua [`mytimer:alarm(interval, repeat, callback)`](modules/tmr/#tmralarm) calls a function in the `tmr` library which registers a C function for this alarm using the SDK, and when this C alarm callback function is called it then in turn invokes the Lua callback. - The Lua libraries provide a set of functions for declaring application functions (written in Lua) as callbacks (which are stored in the [Lua registry](#so-how-is-the-lua-registry-used-and-why-is-this-important)) to associate application tasks with specific hardware and timer events. These are also non-preemptive at an applications level. The Lua libraries work in consort with the SDK to queue pending events and invoke any registered Lua callback routines, which then run to completion uninterrupted. For example the Lua [`mytimer:alarm(interval, repeat, callback)`](modules/tmr/#tmralarm) calls a function in the `tmr` library which registers a C function for this alarm using the SDK, and when this C alarm callback function is called it then in turn invokes the Lua callback.
* Excessively long-running Lua functions (or Lua code chunks executed at the interactive prompt through UART 0) can cause other system functions and services to timeout, or to allocate scarce RAM resources to buffer queued data, which can then trigger either the watchdog timer or memory exhaustion, both of which will ultimately cause the system to reboot. - Excessively long-running Lua functions (or Lua code chunks executed at the interactive prompt through UART 0) can cause other system functions and services to timeout, or to allocate scarce RAM resources to buffer queued data, which can then trigger either the watchdog timer or memory exhaustion, both of which will ultimately cause the system to reboot.
* Just like their C counterparts, Lua tasks initiated by timer, network, GPIO and other callbacks run non pre-emptively to completion before the next task can run, and this includes SDK tasks. Printing to the default serial port is done by the Lua runtime libraries, but SDK services including even a reboot request are run as individual tasks. This is why in the previous example printout out twenty copies of "not quite yet --" before completing and return control the SDK which then allows the reboot to occur. - Just like their C counterparts, Lua tasks initiated by timer, network, GPIO and other callbacks run non pre-emptively to completion before the next task can run, and this includes SDK tasks. Printing to the default serial port is done by the Lua runtime libraries, but SDK services including even a reboot request are run as individual tasks. This is why in the previous example printout out twenty copies of "not quite yet --" before completing and return control the SDK which then allows the reboot to occur.
This event-driven approach is very different to a conventional procedural applications written in Lua, and different from how you develop C sketches and applications for the Arduino architectures. _There is little point in constructing poll loops in your NodeMCU Lua code since almost always the event that you are polling will not be delivered by the SDK until after your Lua code returns control to the SDK._ The most robust and efficient approach to coding ESP8266 Lua applications is to embrace this event model paradigm, and to decompose your application into atomic tasks that are threaded by events which themselves initiate callback functions. Each event task is established by a callback in an API call in an earlier task. This event-driven approach is very different to a conventional procedural applications written in Lua, and different from how you develop C sketches and applications for the Arduino architectures. _There is little point in constructing poll loops in your NodeMCU Lua code since almost always the event that you are polling will not be delivered by the SDK until after your Lua code returns control to the SDK._ The most robust and efficient approach to coding ESP8266 Lua applications is to embrace this event model paradigm, and to decompose your application into atomic tasks that are threaded by events which themselves initiate callback functions. Each event task is established by a callback in an API call in an earlier task.
Understanding how the system executes your code can help you structure it better and improve both performance and memory usage. Understanding how the system executes your code can help you structure it better and improve both performance and memory usage.
* _If you are not using timers and other callback, then you are using the wrong approach._ - _If you are not using timers and other callback, then you are using the wrong approach._
* _If you are using poll loops, then you are using the wrong approach._ - _If you are using poll loops, then you are using the wrong approach._
* _If you are executing more an a few hundred lines of Lua per callback, then you are using the wrong approach._ - _If you are executing more an a few hundred lines of Lua per callback, then you are using the wrong approach._
### So what Lua library functions enable the registration of Lua callbacks? ### So what Lua library functions enable the registration of Lua callbacks?
...@@ -129,87 +143,96 @@ SDK Callbacks include: ...@@ -129,87 +143,96 @@ SDK Callbacks include:
| mqtt | `client:m:on(event, function(conn[, topic, data])` | | mqtt | `client:m:on(event, function(conn[, topic, data])` |
| uart | `uart.on(event, cnt, [function(data)], [run_input])` | | uart | `uart.on(event, cnt, [function(data)], [run_input])` |
For a comprehensive list refer to the Module documentation on this site. For a comprehensive list refer to the module documentation on this site.
### So what are the different ways of declaring variables and how is NodeMCU different here? ### So what are the different ways of declaring variables and how is NodeMCU different here?
The following is all standard Lua and is explained in detail in PiL etc., but it is worth summarising here because understanding this is of particular importance in the NodeMCU environment. The following is all standard Lua and is explained in detail in PiL etc., but it is worth summarising here because understanding this is of particular importance in the NodeMCU environment.
* All variables in Lua can be classed as globals, locals or upvalues. But by default any variable that is referenced and not previously declared as `local` is **global** and this variable will persist in the global table until it is explicitly deleted. If you want to see what global variables are in scope then try All variables in Lua can be classed as globals, locals or upvalues. But by default any variable that is referenced and not previously declared as `local` is **global** and this variable will persist in the global table until it is explicitly deleted. If you want to see what global variables are in scope then try
```Lua ```Lua
for k,v in pairs(_G) do print(k,v) end for k,v in pairs(_G) do print(k,v) end
``` ```
* Local variables are 'lexically scoped', and you may declare any variables as local within nested blocks or functions without affecting the enclosing scope.
* Because locals are lexically scoped you can also refer to local variables in an outer scope and these are still accessible within the inner scope. Such variables are know as **upvalues**.. Local variables are 'lexically scoped', and you may declare any variables as local within nested blocks or functions without affecting the enclosing scope. Because locals are lexically scoped you can also refer to local variables in an outer scope and these are still accessible within the inner scope. Such variables are know as **upvalues**.
* Lua variable can be assigned two broad types of data: **values** such as numbers, booleans, and strings and **references** such as functions, tables and userdata. You can see the difference here when you assign the contents of a variable `a` to `b`. In the case of a value then it is simply copied into `b`. In the case of a reference, both `a` and `b` now refer to the *same object*, and no copying of content takes place. This process of referencing can have some counter-intuitive consequences. For example, in the following code by the time it exists, the variable `timer2func` is out of scope. However a reference to the function has now been stored in the Lua registry by the alarm API call, so it and any upvalues that it uses will persist until it is eventually entirely dereferenced (e.g. by `tmr2:unregister()`.
Lua variable can be assigned two broad types of data: **values** such as numbers, booleans, and strings and **references** such as functions, tables and userdata. You can see the difference here when you assign the contents of a variable `a` to `b`. In the case of a value then it is simply copied into `b`. In the case of a reference, both `a` and `b` now refer to the *same object*, and no copying of content takes place. This process of referencing can have some counter-intuitive consequences. For example, in the following code by the time it exists, the variable `tmr2func` is out of scope. However a reference to the function has now been stored in the Lua registry by the alarm API call, so it and any upvalues that it uses will persist until it is eventually entirely dereferenced (e.g. by `tmr2:unregister()`).
```Lua ```Lua
do do
local tmr2func = function() ds.convert_T(true); tmr1:start() end local tmr2func = function() ds.convert_T(true); tmr1:start() end
tmr2:alarm(300000, tmr.ALARM_AUTO, tmr2func) tmr2:alarm(300000, tmr.ALARM_AUTO, tmr2func)
end end
--
``` ```
* You need to understand the difference between when a function is compiled, when it is bound as a closure and when it is invoked at runtime. The closure is normally bound once pretty much immediately after compile, but this isn't necessarily the case. Consider the following example from my MCP23008 module below.
```Lua
-- Bind the read and write functions for commonly accessed registers
for reg, regAddr in pairs { You need to understand the difference between when a function is compiled, when it is bound as a closure and when it is invoked at runtime. The closure is normally bound once pretty much immediately after compile, but this isn't necessarily the case. Consider the following example from my MCP23008 module below.
```Lua
-- Bind the read and write functions for commonly accessed registers
for reg, regAddr in pairs {
IODOR = 0x00, IODOR = 0x00,
GPPU = 0x06, -- Pull-up resistors register for MCP23008 GPPU = 0x06, -- Pull-up resistors register for MCP23008
GPIO = 0x09, GPIO = 0x09,
OLAT = 0x0A, OLAT = 0x0A,
} do } do
dev['write'..reg] = function(o, dataByte) dev['write' .. reg] = function(o, dataByte)
write(MCP23008addr, regAddr, dataByte) write(MCP23008addr, regAddr, dataByte)
end end
dev['read'..reg] = function(o) dev['read' .. reg] = function(o)
return read(MCP23008addr, regAddr) return read(MCP23008addr, regAddr)
end end
end end
``` ```
* This loop is compiled once when the module is required. The opcode vectors for the read and write functions are created during the compile, along with a header which defines how many upvalues and locals are used by each function. However, these two functions are then bound _four_ times as different functions (e.g. `mcp23008.writeIODOR()`) and each closure inherits its own copies of the upvalues it uses so the `regAddr` for this function is `0x00`). The upvalue list is created when the closure is created and through some Lua magic, even if the outer routine that initially declared them is no longer in scope and has been GCed (Garbage Collected), the Lua RTS ensures that any upvalue will still persist whilst the closure persists.
* On the other hand the storage for any locals is allocated each time the routine is called, and this can be many times in a running application. This loop is compiled once when the module is required. The opcode vectors for the read and write functions are created during the compile, along with a header which defines how many upvalues and locals are used by each function. However, these two functions are then bound _four_ times as different functions (e.g. `mcp23008.writeIODOR()`) and each closure inherits its own copies of the upvalues it uses so the `regAddr` for this function is `0x00`). The upvalue list is created when the closure is created and through some Lua magic, even if the outer routine that initially declared them is no longer in scope and has been GCed (Garbage Collected), the Lua RTS ensures that any upvalue will still persist whilst the closure persists.
* The Lua runtime uses hashed key access internally to retrieve keyed data from a table. On the other hand locals and upvalues are stored as a contiguous vector and are accessed directly by an index, which is a lot faster. In NodeMCU Lua accesses to Firmware-based tables is particularly slow, which is why you will often see statements like the following at the beginning of modules. *Using locals and upvalues this way is both a lot faster at runtime and generates less bytecode instructions for their access.*
On the other hand the storage for any locals is allocated each time the routine is called, and this can be many times in a running application.
The Lua runtime uses hashed key access internally to retrieve keyed data from a table. On the other hand locals and upvalues are stored as a contiguous vector and are accessed directly by an index, which is a lot faster. In NodeMCU Lua accesses to Firmware-based tables is particularly slow, which is why you will often see statements like the following at the beginning of modules. *Using locals and upvalues this way is both a lot faster at runtime and generates less bytecode instructions for their access.*
```Lua ```Lua
local i2c = i2c local i2c = i2c
local i2c_start, i2c_stop, i2c_address, i2c_read, i2c_write, i2c_TRANSMITTER, i2c_RECEIVER = local i2c_start, i2c_stop, i2c_address, i2c_read, i2c_write, i2c_TRANSMITTER, i2c_RECEIVER =
i2c.start, i2c.stop, i2c.address, i2c.read, i2c.write, i2c.TRANSMITTER, i2c.RECEIVER i2c.start, i2c.stop, i2c.address, i2c.read, i2c.write, i2c.TRANSMITTER, i2c.RECEIVER
``` ```
* I will cover some useful Global and Upvalue techniques in later Qs.
### So how is context passed between Lua event tasks? ### So how is context passed between Lua event tasks?
* It is important to understand that a single Lua function is associated with / bound to any event callback task. This function is executed from within the relevant NodeMCU library C code using a `lua_call()`. Even system initialisation which executes the `dofile("init.lua")` is really a special case of this. Each function can invoke other functions and so on, but it must ultimately return control to the C library code which then returns control the SDK, terminating the task. It is important to understand that a single Lua function is associated with / bound to any event callback task. This function is executed from within the relevant NodeMCU library C code using a `lua_call()`. Even system initialisation which executes the `dofile("init.lua")` is really a special case of this. Each function can invoke other functions and so on, but it must ultimately return control to the C library code which then returns control the SDK, terminating the task.
* By their very nature Lua `local` variables only exist within the context of an executing Lua function, and so locals are unreferenced on exit and any local data (unless also a reference type such as a function, table, or user data which is also referenced elsewhere) can therefore be garbage collected between these `lua_call()` actions.
By their very nature Lua `local` variables only exist within the context of an executing Lua function, and so locals are unreferenced on exit and any local data (unless also a reference type such as a function, table, or user data which is also referenced elsewhere) can therefore be garbage collected between these `lua_call()` actions.
So context can only be passed between event routines by one of the following mechanisms: So context can only be passed between event routines by one of the following mechanisms:
* **Globals** are by nature globally accessible. Any global will persist until explicitly dereferenced by assigning `nil` to it. Globals can be readily enumerated, e.g. by a `for k,v in pairs(_G) do`, so their use is transparent. - **Globals** are by nature globally accessible. Any global will persist until explicitly dereferenced by assigning `nil` to it. Globals can be readily enumerated, e.g. by a `for k,v in pairs(_G) do`, so their use is transparent.
* The **File system** is a special case of persistent global, so there is no reason in principle why it and the files it contains can't be used to pass context. However the ESP8266 file system uses flash memory and even with the SPIFFS file system still has a limited write cycle lifetime, so it is best to avoid using the file system to store frequently changing content except as a mechanism of last resort. - The **File system** is a special case of persistent global, so there is no reason in principle why it and the files it contains can't be used to pass context. However the ESP8266 file system uses flash memory and even with the SPIFFS file system still has a limited write cycle lifetime, so it is best to avoid using the file system to store frequently changing content except as a mechanism of last resort.
* The **Lua Registry**. This is a normally hidden table used by the library modules to store callback functions and other Lua data types. The GC treats the registry as in scope and hence any content referenced in the registry will not be garbage collected. - The **Lua Registry**. This is a normally hidden table used by the library modules to store callback functions and other Lua data types. The GC treats the registry as in scope and hence any content referenced in the registry will not be garbage collected.
* **Upvalues**. These are a standard feature of Lua as described above that is fully implemented in NodeMCU. When a function is declared within an outer function, all of the local variables within the outer scope are available to the inner function. Ierusalimschy's paper, [Closures in Lua](http://www.cs.tufts.edu/~nr/cs257/archive/roberto-ierusalimschy/closures-draft.pdf), gives a lot more detail for those that want to dig deeper. - **Upvalues**. These are a standard feature of Lua as described above that is fully implemented in NodeMCU. When a function is declared within an outer function, all of the local variables within the outer scope are available to the inner function. Ierusalimschy's paper, [Closures in Lua](http://www.cs.tufts.edu/~nr/cs257/archive/roberto-ierusalimschy/closures-draft.pdf), gives a lot more detail for those that want to dig deeper.
### So how is the Lua Registry used and why is this important? ### So how is the Lua Registry used and why is this important?
All Lua callbacks are called by C wrapper functions within the NodeMCU libraries that are themselves callbacks that have been activated by the SDK as a result of a given event. Such C wrapper functions themselves frequently need to store state for passing between calls or to other wrapper C functions. The Lua registry is a special Lua table which is used for this purpose, except that it is hidden from direct Lua access, but using a standard Lua table for this store enables standard garbage collection algorithms to operate on its content. Any content that needs to be saved is created with a unique key. The upvalues for functions that are global or referenced in the Lua Registry will persist between event routines, and hence any upvalues used by them will also persist and can be used for passing context. All Lua callbacks are called by C wrapper functions within the NodeMCU libraries that are themselves callbacks that have been activated by the SDK as a result of a given event. Such C wrapper functions themselves frequently need to store state for passing between calls or to other wrapper C functions. The Lua registry is a special Lua table which is used for this purpose, except that it is hidden from direct Lua access, but using a standard Lua table for this store enables standard garbage collection algorithms to operate on its content. Any content that needs to be saved is created with a unique key. The upvalues for functions that are global or referenced in the Lua Registry will persist between event routines, and hence any upvalues used by them will also persist and can be used for passing context.
* If you are running out of memory, then you might not be correctly clearing down Registry entries. One example is as above where you are setting up timers but not unregistering them. Another occurs in the following code fragment. The `on()` function passes the socket to the connection callback as it's first argument `sck`. This is local variable in the callback function, and it also references the same socket as the upvalue `srv`. So functionally `srv` and `sck` are interchangeable. So why pass it as an argument? Normally garbage collecting a socket will automatically unregister any of its callbacks, but if you use a socket as an upvalue in the callback, the socket is now referenced through the Register, and now it won't be GCed because it is referenced. Catch-22 and a programming error, not a bug. If you are running out of memory, then you might not be correctly clearing down Registry entries. One example is as above where you are setting up timers but not unregistering them. Another occurs in the following code fragment. The `on()` function passes the socket to the connection callback as it's first argument `sck`. This is local variable in the callback function, and it also references the same socket as the upvalue `srv`. So functionally `srv` and `sck` are interchangeable. So why pass it as an argument? Normally garbage collecting a socket will automatically unregister any of its callbacks, but if you use a socket as an upvalue in the callback, the socket is now referenced through the Register, and now it won't be GCed because it is referenced. Catch-22 and a programming error, not a bug.
Example of wrong upvalue usage in the callback:
```Lua ```Lua
srv:on("connection", function(sck, c) srv:on("connection", function(sck, c)
svr:send(reply) svr:send(reply) -- should be 'sck' instead of 'srv'
end) end)
``` ```
* One way to check the registry is to use the construct `for k,v in pairs(debug.getregistry()) do print (k,v) end` to track the registry size. If this is growing then you've got a leak. Examples of correct callback implementations can be found in the [net socket documentation](modules/net.md#netsocketon).
### How do I track globals
* See the Unofficial LUA FAQ: [Detecting Undefined Variables](http://lua-users.org/wiki/DetectingUndefinedVariables). One way to check the registry is to use the construct `for k,v in pairs(debug.getregistry()) do print (k,v) end` to track the registry size. If this is growing then you've got a leak.
* My approach is to avoid using them unless I have a *very* good reason to justify this. I track them statically by running a `luac -p -l XXX.lua | grep GLOBAL` filter on any new modules and replace any accidental globals by local or upvalued local declarations. ### How do I track globals
* On NodeMCU, _G's metatable is _G, so you can create any globals that you need and then 'close the barn door' by assigning - See the Unofficial Lua FAQ: [Detecting Undefined Variables](http://lua-users.org/wiki/DetectingUndefinedVariables).
- My approach is to avoid using them unless I have a _very_ good reason to justify this. I track them statically by running a `luac -p -l XXX.lua | grep GLOBAL` filter on any new modules and replace any accidental globals by local or upvalued local declarations.
- On NodeMCU, _G's metatable is _G, so you can create any globals that you need and then 'close the barn door' by assigning
`_G.__newindex=function(g,k,v) error ("attempting to set global "..k.." to "..v) end` and any attempt to create new globals with now throw an error and give you a traceback of where this has happened. `_G.__newindex=function(g,k,v) error ("attempting to set global "..k.." to "..v) end` and any attempt to create new globals with now throw an error and give you a traceback of where this has happened.
### Why is it importance to understand how upvalues are implemented when programming for the ESP8266? ### Why is it importance to understand how upvalues are implemented when programming for the ESP8266?
...@@ -225,24 +248,26 @@ One further complication is that some library functions don't implicitly derefer ...@@ -225,24 +248,26 @@ One further complication is that some library functions don't implicitly derefer
### Can I encapsulate actions such as sending an email in a Lua function? ### Can I encapsulate actions such as sending an email in a Lua function?
Think about the implications of these last few answers. Think about the implications of these last few answers.
* An action such as composing and sending an email involves a message dialogue with a mail server over TCP. This in turn requires calling multiple API calls to the SDK and your Lua code must return control to the C calling library for this to be scheduled, otherwise these requests will just queue up, you'll run out of RAM and your application will PANIC.
* Hence it is simply ***impossible*** to write a Lua module so that you can do something like: An action such as composing and sending an email involves a message dialogue with a mail server over TCP. This in turn requires calling multiple API calls to the SDK and your Lua code must return control to the C calling library for this to be scheduled, otherwise these requests will just queue up, you'll run out of RAM and your application will PANIC. Hence it is simply **impossible** to write a Lua module so that you can do something like:
```lua ```lua
-- prepare message -- prepare message
status = mail.send(to, subject, body) status = mail.send(to, subject, body)
-- move on to next phase of processing. -- move on to next phase of processing.
``` ```
* But you could code up a event-driven task to do this and pass it a callback to be executed on completion of the mail send, something along the lines of the following. Note that since this involves a lot of asynchronous processing and which therefore won't take place until you've returned control to the calling library C code, you will typically execute this as the last step in a function and therefore this is best done as a tailcall [PiL 6.3].
But you could code up a event-driven task to do this and pass it a callback to be executed on completion of the mail send, something along the lines of the following. Note that since this involves a lot of asynchronous processing and which therefore won't take place until you've returned control to the calling library C code, you will typically execute this as the last step in a function and therefore this is best done as a tailcall [PiL 6.3].
```lua ```lua
-- prepare message -- prepare message
local ms = require("mail_sender") local ms = require("mail_sender")
return ms.send(to, subject, body, function(status) return ms.send(to, subject, body, function(status)
loadfile("process_next.lua")(status) loadfile("process_next.lua")(status)
end) end)
``` ```
* Building an application on the ESP8266 is a bit like threading pearls onto a necklace. Each pearl is an event task which must be small enough to run within its RAM resources and the string is the variable context that links the pearls together.
Building an application on the ESP8266 is a bit like threading pearls onto a necklace. Each pearl is an event task which must be small enough to run within its RAM resources and the string is the variable context that links the pearls together.
### When and why should I avoid using tmr.delay()? ### When and why should I avoid using tmr.delay()?
...@@ -256,109 +281,121 @@ It will achieve no functional purpose in pretty much every other usecase, as any ...@@ -256,109 +281,121 @@ It will achieve no functional purpose in pretty much every other usecase, as any
Most of us have fallen into the trap of creating an `init.lua` that has a bug in it, which then causes the system to reboot and hence gets stuck in a reboot loop. If you haven't then you probably will do so at least once. Most of us have fallen into the trap of creating an `init.lua` that has a bug in it, which then causes the system to reboot and hence gets stuck in a reboot loop. If you haven't then you probably will do so at least once.
- When this happens, the only robust solution is to reflash the firmware. When this happens, the only robust solution is to reflash the firmware.
- The simplest way to avoid having to do this is to keep the `init.lua` as simple as possible -- say configure the wifi and then start your app using a one-time `tmr.alarm()` after a 2-3 sec delay. This delay is long enough to issue a `file.remove("init.lua")` through the serial port and recover control that way.
- Another trick is to poll a spare GPIO input pin in your startup. I do this on my boards by taking this GPIO plus Vcc to a jumper on the board, so that I can set the jumper to jump into debug mode or reprovision the software. The simplest way to avoid having to do this is to keep the `init.lua` as simple as possible -- say configure the wifi and then start your app using a one-time `tmr.alarm()` after a 2-3 sec delay. This delay is long enough to issue a `file.remove("init.lua")` through the serial port and recover control that way.
- Also it is always best to test any new `init.lua` by creating it as `init_test.lua`, say, and manually issuing a `dofile("init_test.lua")` through the serial port, and then only rename it when you are certain it is working as you require.
Another trick is to poll a spare GPIO input pin in your startup. I do this on my boards by taking this GPIO plus Vcc to a jumper on the board, so that I can set the jumper to jump into debug mode or reprovision the software.
See ["Uploading code" → init.lua](upload.md#initlua) for an example. Also it is always best to test any new `init.lua` by creating it as `init_test.lua`, say, and manually issuing a `dofile("init_test.lua")` through the serial port, and then only rename it when you are certain it is working as you require.
See ["Uploading code" → init.lua](upload.md#initlua) for a very detaild example.
## Compiling and Debugging ## Compiling and Debugging
* We recommend that you install Lua 5.1 on your delopment host. This often is useful for debugging Lua fragments on your PC. You also use it for compile validation. We recommend that you install Lua 5.1 on your development host. This often is useful for debugging Lua fragments on your PC. You also use it for compile validation.
* You can also build `luac.cross` on your development host if you have Lua locally installed. This runs on your host and has all of the features of standard `luac`, except that the output code file will run under NodeMCU as an *lc* file. You can also build `luac.cross` on your development host if you have Lua locally installed. This runs on your host and has all of the features of standard `luac`, except that the output code file will run under NodeMCU as an _lc_ file.
## Techniques for Reducing RAM and SPIFFS footprint ## Techniques for Reducing RAM and SPIFFS footprint
### How do I minimise the footprint of an application? ### How do I minimise the footprint of an application?
* Perhaps the simplest aspect of reducing the footprint of an application is to get its scope correct. The ESP8266 is an IoT device and not a general purpose system. It is typically used to attach real-world monitors, controls, etc. to an intranet and is therefore designed to implement functions that have limited scope. We commonly come across developers who are trying to treat the ESP8266 as a general purpose device and can't understand why their application can't run. Perhaps the simplest aspect of reducing the footprint of an application is to get its scope correct. The ESP8266 is an IoT device and not a general purpose system. It is typically used to attach real-world monitors, controls, etc. to an intranet and is therefore designed to implement functions that have limited scope. We commonly come across developers who are trying to treat the ESP8266 as a general purpose device and can't understand why their application can't run.
* The simplest and safest way to use IoT devices is to control them through a dedicated general purpose system on the same network. This could be a low cost system such as a [RaspberryPi (RPi)](https://www.raspberrypi.org/) server, running your custom code or an open source home automation (HA) application. Such systems have orders of magnitude more capacity than the ESP8266, for example the RPi has 2GB RAM and its SD card can be up to 32GB in capacity, and it can support the full range of USB-attached disk drives and other devices. It also runs a fully featured Linux OS, and has a rich selection of applications pre configured for it. There are plenty of alternative systems available in this under $50 price range, as well as proprietary HA systems which can cost 10-50 times more.
* Using a tiered approach where all user access to the ESP8266 is passed through a controlling server means that the end-user interface (or smartphone connector), together with all of the associated validation and security can be implemented on a system designed to have the capacity to do this. This means that you can limit the scope of your ESP8266 application to a limited set of functions being sent to or responding to requests from this system. The simplest and safest way to use IoT devices is to control them through a dedicated general purpose system on the same network. This could be a low cost system such as a [RaspberryPi (RPi)](https://www.raspberrypi.org/) server, running your custom code or an open source home automation (HA) application. Such systems have orders of magnitude more capacity than the ESP8266, for example the RPi has 2GB RAM and its SD card can be up to 32GB in capacity, and it can support the full range of USB-attached disk drives and other devices. It also runs a fully featured Linux OS, and has a rich selection of applications pre configured for it. There are plenty of alternative systems available in this under $50 price range, as well as proprietary HA systems which can cost 10-50 times more.
* *If you are trying to implement a user-interface or HTTP webserver in your ESP8266 then you are really abusing its intended purpose. When it comes to scoping your ESP8266 applications, the adage **K**eep **I**t **S**imple **S**tupid truly applies.*
Using a tiered approach where all user access to the ESP8266 is passed through a controlling server means that the end-user interface (or smartphone connector), together with all of the associated validation and security can be implemented on a system designed to have the capacity to do this. This means that you can limit the scope of your ESP8266 application to a limited set of functions being sent to or responding to requests from this system.
_If you are trying to implement a user-interface or HTTP webserver in your ESP8266 then you are really abusing its intended purpose. When it comes to scoping your ESP8266 applications, the adage **K**eep **I**t **S**imple **S**tupid truly applies._
### How do I minimise the footprint of an application on the file system ### How do I minimise the footprint of an application on the file system
* It is possible to write Lua code in a very compact format which is very dense in terms of functionality per KB of source code. - It is possible to write Lua code in a very compact format which is very dense in terms of functionality per KB of source code.
* However if you do this then you will also find it extremely difficult to debug or maintain your application. - However if you do this then you will also find it extremely difficult to debug or maintain your application.
* A good compromise is to use a tool such as [LuaSrcDiet](http://luaforge.net/projects/luasrcdiet/), which you can use to compact production code for downloading to the ESP8266: - A good compromise is to use a tool such as [LuaSrcDiet](http://luaforge.net/projects/luasrcdiet/), which you can use to compact production code for downloading to the ESP8266:
* Keep a master repository of your code on your PC or a cloud-based versioning repository such as [GitHub](https://github.com/) - Keep a master repository of your code on your PC or a cloud-based versioning repository such as [GitHub](https://github.com/)
* Lay it out and comment it for ease of maintenance and debugging - Lay it out and comment it for ease of maintenance and debugging
* Use a package such as [Esplorer](https://github.com/4refr0nt/ESPlorer) to download modules that you are debugging and to test them. - Use a package such as [Esplorer](https://github.com/4refr0nt/ESPlorer) to download modules that you are debugging and to test them.
* Once the code is tested and stable, then compress it using LuaSrcDiet before downloading to the ESP8266. Doing this will reduce the code footprint on the SPIFFS by 2-3x. Also note that LuaSrcDiet has a mode which achieves perhaps 95% of the possible code compaction but which still preserves line numbering. This means that any line number-based error messages will still be usable. - Once the code is tested and stable, then compress it using LuaSrcDiet before downloading to the ESP8266. Doing this will reduce the code footprint on the SPIFFS by 2-3x. Also note that LuaSrcDiet has a mode which achieves perhaps 95% of the possible code compaction but which still preserves line numbering. This means that any line number-based error messages will still be usable.
* Standard Lua compiled code includes a lot of debug information which almost doubles its RAM size. [node.stripdebug()](modules/node.md#nodestripdebug) can be used to change this default setting either to increase the debug information for a given module or to remove line number information to save a little more space. Using `node.compile()` to pre-compile any production code will remove all compiled code including error line info and so is not recommended except for stable production code where line numbers are not needed. - Standard Lua compiled code includes a lot of debug information which almost doubles its RAM size. [node.stripdebug()](modules/node.md#nodestripdebug) can be used to change this default setting either to increase the debug information for a given module or to remove line number information to save a little more space. Using `node.compile()` to pre-compile any production code will remove all compiled code including error line info and so is not recommended except for stable production code where line numbers are not needed.
### How do I minimise the footprint of running application? ### How do I minimise the footprint of running application?
* The Lua Garbage collector is very aggressive at scanning and recovering dead resources. It uses an incremental mark-and-sweep strategy which means that any data which is not ultimately referenced back to the Globals table, the Lua registry or in-scope local variables in the current Lua code will be collected. The Lua Garbage collector is very aggressive at scanning and recovering dead resources. It uses an incremental mark-and-sweep strategy which means that any data which is not ultimately referenced back to the Globals table, the Lua registry or in-scope local variables in the current Lua code will be collected.
* Setting any variable to `nil` dereferences the previous context of that variable. (Note that reference-based variables such as tables, strings and functions can have multiple variables referencing the same object, but once the last reference has been set to `nil`, the collector will recover the storage.
* Unlike other compile-on-load languages such as PHP, Lua compiled code is treated the same way as any other variable type when it comes to garbage collection and can be collected when fully dereferenced, so that the code-space can be reused. Setting any variable to `nil` dereferences the previous context of that variable. (Note that reference-based variables such as tables, strings and functions can have multiple variables referencing the same object, but once the last reference has been set to `nil`, the collector will recover the storage.
* The default garbage collection mode is very aggressive and results in a GC sweep after every allocation. See [node.egc.setmode()](modules/node/#nodeegcsetmode) for how to turn this down. `node.egc.setmode(node.egc.ON_MEM_LIMIT, 4096)` is a good compromise of performance and having enough free headboard.
* Lua execution is intrinsically divided into separate event tasks with each bound to a Lua callback. This, when coupled with the strong dispose on dereference feature, means that it is very easy to structure your application using an classic technique which dates back to the 1950s known as Overlays. Unlike other compile-on-load languages such as PHP, Lua compiled code is treated the same way as any other variable type when it comes to garbage collection and can be collected when fully dereferenced, so that the code-space can be reused.
* Various approaches can be use to implement this. One is described by DP Whittaker in his [Massive memory optimization: flash functions](http://www.esp8266.com/viewtopic.php?f=19&t=1940) topic. Another is to use *volatile modules*. There are standard Lua templates for creating modules, but the `require()` library function creates a reference for the loaded module in the `package.loaded` table, and this reference prevents the module from being garbage collected. To make a module volatile, you should remove this reference to the loaded module by setting its corresponding entry in `package.loaded` to `nil`. You can't do this in the outermost level of the module (since the reference is only created once execution has returned from the module code), but you can do it in any module function, and typically an initialisation function for the module, as in the following example:
The default garbage collection mode is very aggressive and results in a GC sweep after every allocation. See [node.egc.setmode()](modules/node/#nodeegcsetmode) for how to turn this down. `node.egc.setmode(node.egc.ON_MEM_LIMIT, 4096)` is a good compromise of performance and having enough free headboard.
Lua execution is intrinsically divided into separate event tasks with each bound to a Lua callback. This, when coupled with the strong dispose on dereference feature, means that it is very easy to structure your application using an classic technique which dates back to the 1950s known as Overlays.
Various approaches can be use to implement this. One is described by DP Whittaker in his [Massive memory optimization: flash functions](http://www.esp8266.com/viewtopic.php?f=19&t=1940) topic. Another is to use *volatile modules*. There are standard Lua templates for creating modules, but the `require()` library function creates a reference for the loaded module in the `package.loaded` table, and this reference prevents the module from being garbage collected. To make a module volatile, you should remove this reference to the loaded module by setting its corresponding entry in `package.loaded` to `nil`. You can't do this in the outermost level of the module (since the reference is only created once execution has returned from the module code), but you can do it in any module function, and typically an initialisation function for the module, as in the following example:
```lua ```lua
local s=net.createServer(net.TCP) local s = net.createServer(net.TCP)
s:listen(80,function(c) require("connector").init(c) end) s:listen(80, function(c) require("connector").init(c) end)
``` ```
* **`connector.lua`** would be a standard module pattern except that the `M.init()` routine must include the lines
`connector.lua` would be a standard module pattern except that the `M.init()` routine must include the lines
```lua ```lua
local M, module = {}, ... local M, module = {}, ......
...
function M.init(csocket) function M.init(csocket)
package.loaded[module]=nil package.loaded[module] = nil...
...
end end
--
return M return M
``` ```
* This approach ensures that the module can be fully dereferenced on completion. OK, in this case, this also means that the module has to be reloaded on each TCP connection to port 80; however, loading a compiled module from SPIFFS only takes a few mSec, so surely this is an acceptable overhead if it enables you to break down your application into RAM-sized chunks. Note that `require()` will automatically search for `connector.lc` followed by `connector.lua`, so the code will work for both source and compiled variants. This approach ensures that the module can be fully dereferenced on completion. OK, in this case, this also means that the module has to be reloaded on each TCP connection to port 80; however, loading a compiled module from SPIFFS only takes a few mSec, so surely this is an acceptable overhead if it enables you to break down your application into RAM-sized chunks. Note that `require()` will automatically search for `connector.lc` followed by `connector.lua`, so the code will work for both source and compiled variants.
* Whilst the general practice is for a module to return a table, [PiL 15.1] suggests that it is sometimes appropriate to return a single function instead as this avoids the memory overhead of an additional table. This pattern would look as follows: - Whilst the general practice is for a module to return a table, [PiL 15.1] suggests that it is sometimes appropriate to return a single function instead as this avoids the memory overhead of an additional table. This pattern would look as follows:
```lua ```lua
-- local s = net.createServer(net.TCP)
local s=net.createServer(net.TCP) s:listen(80, function(c) require("connector")(c) end)
s:listen(80,function(c) require("connector")(c) end)
``` ```
```lua ```lua
local module = _ -- this is a situation where using an upvalue is essential! local module = _ -- this is a situation where using an upvalue is essential!
return function (csocket) return function(csocket)
package.loaded[module]=nil package.loaded[module] = nil
module = nil module = nil...
...
end end
``` ```
* Also note that you should ***not*** normally code this up listener call as the following because the RAM now has to accommodate both the module which creates the server *and* the connector logic.
Also note that you should **not** normally code this up listener call as the following because the RAM now has to accommodate both the module which creates the server _and_ the connector logic.
```lua ```lua
... ...
local s=net.createServer(net.TCP) local s = net.createServer(net.TCP)
local connector = require("connector") -- don't do this unless you've got the RAM available! local connector = require("connector") -- don't do this unless you've got the RAM available!
s:listen(80,connector) s:listen(80, connector)
``` ```
### How do I reduce the size of my compiled code? ### How do I reduce the size of my compiled code?
Note that there are two methods of saving compiled Lua to SPIFFS: Note that there are two methods of saving compiled Lua to SPIFFS:
- The first is to use `node.compile()` on the `.lua` source file, which generates the equivalent bytecode `.lc` file. This approach strips out all the debug line and variable information.
- The second is to use `loadfile()` to load the source file into memory, followed by `string.dump()` to convert it in-memory to a serialised load format which can then be written back to a `.lc` file. The amount of debug saved will depend on the [node.stripdebug()](modules/node.md#nodestripdebug) settings.
The memory footprint of the bytecode created by method (2) is the same as when executing source files directly, but the footprint of bytecode created by method (1) is typically 10% smaller than a dump with the stripdebug level of 2 or 60% smaller than a dump with a stripdebug level of 0, because the debug information is almost as large as the code itself. - The first is to use `node.compile()` on the `.lua` source file, which generates the equivalent bytecode `.lc` file. This approach strips out all the debug line and variable information.
- The second is to use `loadfile()` to load the source file into memory, followed by `string.dump()` to convert it in-memory to a serialised load format which can then be written back to a `.lc` file. The amount of debug saved will depend on the [node.stripdebug()](modules/node.md#nodestripdebug) settings.
In general consider method (1) if you have stable production code that you want to run in as low a RAM footprint as possible. Yes, method (2) can be used if you are still debugging, but you will probably be changing this code quite frequently, so it is easier to stick with `.lua` files for code that you are still developing. The memory footprint of the bytecode created by method (3) is the same as when executing source files directly, but the footprint of bytecode created by method (2) is typically 10% smaller than a dump with the stripdebug level of 3 or 60% smaller than a dump with a stripdebug level of 1, because the debug information is almost as large as the code itself.
In general consider method (2) if you have stable production code that you want to run in as low a RAM footprint as possible. Yes, method (3) can be used if you are still debugging, but you will probably be changing this code quite frequently, so it is easier to stick with `.lua` files for code that you are still developing.
Note that if you use `require("XXX")` to load your code then this will automatically search for `XXX.lc` then `XXX.lua` so you don't need to include the conditional logic to load the bytecode version if it exists, falling back to the source version otherwise. Note that if you use `require("XXX")` to load your code then this will automatically search for `XXX.lc` then `XXX.lua` so you don't need to include the conditional logic to load the bytecode version if it exists, falling back to the source version otherwise.
### How do I get a feel for how much memory my functions use? ### How do I get a feel for how much memory my functions use?
* You should get an overall understanding of the VM model if you want to make good use of the limited resources available to Lua applications. An essential reference here is [A No Frills Introduction to Lua 5.1 VM Instructions](http://luaforge.net/docman/83/98/ANoFrillsIntroToLua51VMInstructions.pdf) . This explain how the code generator works, how much memory overhead is involved with each table, function, string etc.. You should get an overall understanding of the VM model if you want to make good use of the limited resources available to Lua applications. An essential reference here is [A No Frills Introduction to Lua 5.1 VM Instructions](http://luaforge.net/docman/83/98/ANoFrillsIntroToLua51VMInstructions.pdf) . This explain how the code generator works, how much memory overhead is involved with each table, function, string etc..
* You can't easily get a bytecode listing of your ESP8266 code; however there are two broad options for doing this:
* **Generate a bytecode listing on your development PC**. The Lua 5.1 code generator is basically the same on the PC and on the ESP8266, so whilst it isn't identical, using the standard Lua batch compiler `luac` against your source on your PC with the `-l -s` option will give you a good idea of what your code will generate. The main difference between these two variants is the size_t for ESP8266 is 4 bytes rather than the 8 bytes size_t found on modern 64bit development PCs; and the eLua variants generate different access references for ROM data types. If you want to see what the `string.dump()` version generates then drop the `-s` option to retain the debug information. You can also build `luac.cross` with this firmware and this generate lc code for the target ESP architecture. You can't easily get a bytecode listing of your ESP8266 code; however there are two broad options for doing this:
* **Upload your `.lc` files to the PC and disassemble them there**. There are a number of Lua code disassemblers which can list off the compiled code that your application modules will generate, `if` you have a script to upload files from your ESP8266 to your development PC. I use [ChunkSpy](http://luaforge.net/projects/chunkspy/) which can be downloaded [here](http://files.luaforge.net/releases/chunkspy/chunkspy/ChunkSpy-0.9.8/ChunkSpy-0.9.8.zip) , but you will need to apply the following patch so that ChunkSpy understands eLua data types:
- **Generate a bytecode listing on your development PC**. The Lua 5.1 code generator is basically the same on the PC and on the ESP8266, so whilst it isn't identical, using the standard Lua batch compiler `luac` against your source on your PC with the `-l -s` option will give you a good idea of what your code will generate. The main difference between these two variants is the size_t for ESP8266 is 4 bytes rather than the 8 bytes size_t found on modern 64bit development PCs; and the eLua variants generate different access references for ROM data types. If you want to see what the `string.dump()` version generates then drop the `-s` option to retain the debug information. You can also build `luac.cross` with this firmware and this generate lc code for the target ESP architecture.
- **Upload your `.lc` files to the PC and disassemble them there**. There are a number of Lua code disassemblers which can list off the compiled code that your application modules will generate, `if` you have a script to upload files from your ESP8266 to your development PC. I use [ChunkSpy](http://luaforge.net/projects/chunkspy/) which can be downloaded [here](http://files.luaforge.net/releases/chunkspy/chunkspy/ChunkSpy-0.9.8/ChunkSpy-0.9.8.zip) , but you will need to apply the following patch so that ChunkSpy understands eLua data types:
```diff ```diff
--- a/ChunkSpy-0.9.8/5.1/ChunkSpy.lua 2015-05-04 12:39:01.267975498 +0100 --- a/ChunkSpy-0.9.8/5.1/ChunkSpy.lua 2015-05-04 12:39:01.267975498 +0100
...@@ -373,20 +410,22 @@ Note that if you use `require("XXX")` to load your code then this will automatic ...@@ -373,20 +410,22 @@ Note that if you use `require("XXX")` to load your code then this will automatic
elseif a == "--interact" then elseif a == "--interact" then
perform = ChunkSpy_Interact perform = ChunkSpy_Interact
``` ```
* Your other great friend is to use `node.heap()` regularly through your code.
* Use these tools and play with coding approaches to see how many instructions each typical line of code takes in your coding style. The Lua Wiki gives some general optimisation tips, but in general just remember that these focus on optimising for execution speed and you will be interested mainly in optimising for code and variable space as these are what consumes precious RAM. Your other great friend is to use `node.heap()` regularly through your code.
Use these tools and play with coding approaches to see how many instructions each typical line of code takes in your coding style. The Lua Wiki gives some general optimisation tips, but in general just remember that these focus on optimising for execution speed and you will be interested mainly in optimising for code and variable space as these are what consumes precious RAM.
### What is the cost of using functions? ### What is the cost of using functions?
Functions have fixed overheads, so in general the more that you group your application code into larger functions, then the less RAM used will be used overall. The main caveat here is that if you are starting to do "copy and paste" coding across functions then you are wasting resources. So of course you should still use functions to structure your code and encapsulate common repeated processing, but just bear in mind that each function definition has a relatively high overhead for its header record and stack frame. *So try to avoid overusing functions. If there are less than a dozen or so lines in the function then you should consider putting this code inline if it makes sense to do so.* Functions have fixed overheads, so in general the more that you group your application code into larger functions, then the less RAM used will be used overall. The main caveat here is that if you are starting to do "copy and paste" coding across functions then you are wasting resources. So of course you should still use functions to structure your code and encapsulate common repeated processing, but just bear in mind that each function definition has a relatively high overhead for its header record and stack frame. _So try to avoid overusing functions. If there are less than a dozen or so lines in the function then you should consider putting this code inline if it makes sense to do so._
### What other resources are available? ### What other resources are available?
* Install lua and luac on your development PC. This is freely available for Windows, Mac and Linux distributions, but we strongly suggest that you use Lua 5.1 to maintain source compatibility with ESP8266 code. This will allow you not only to unit test some modules on your PC in a rich development environment, but you can also use `luac` to generate a bytecode listing of your code and to validate new code syntactically before downloading to the ESP8266. This will also allow you to develop server-side applications and embedded applications in a common language. Install `lua` and `luac` on your development PC. This is freely available for Windows, Mac and Linux distributions, but we strongly suggest that you use Lua 5.1 to maintain source compatibility with ESP8266 code. This will allow you not only to unit test some modules on your PC in a rich development environment, but you can also use `luac` to generate a bytecode listing of your code and to validate new code syntactically before downloading to the ESP8266. This will also allow you to develop server-side applications and embedded applications in a common language.
## Firmware and Lua app development ## Firmware and Lua app development
### How to reduce the size of the firmware? ### How to reduce the size of the firmware?
* We recommend that you use a tailored firmware build; one which only includes the modules that you plan to use in developing any Lua application. Once you have the ability to make and flash custom builds, the you also have the option of moving time sensitive or logic intensive code into your own custom module. Doing this can save a large amount of RAM as C code can be run directly from Flash memory. See [Building the firmware](../build/) for more details and options. We recommend that you use a tailored firmware build; one which only includes the modules that you plan to use in developing any Lua application. Once you have the ability to make and flash custom builds, the you also have the option of moving time sensitive or logic intensive code into your own custom module. Doing this can save a large amount of RAM as C code can be run directly from Flash memory. See [Building the firmware](../build/) for more details and options.
...@@ -3,61 +3,154 @@ ...@@ -3,61 +3,154 @@
| :----- | :-------------------- | :---------- | :------ | | :----- | :-------------------- | :---------- | :------ |
| 2017-04-24 | [fetchbot](https://github.com/fetchbot) | [fetchbot](https://github.com/fetchbot) | [ads1115.c](../../../app/modules/ads1115.c)| | 2017-04-24 | [fetchbot](https://github.com/fetchbot) | [fetchbot](https://github.com/fetchbot) | [ads1115.c](../../../app/modules/ads1115.c)|
This module provides access to the ADS1115 16-Bit analog-to-digital converter. This module provides access to the ADS1115 (16-Bit) and ADS1015 (12-Bit) analog-to-digital converters.
Other chips from the same family (ADS1113, ADS1114, ADS1013 and ADS1014) are likely to work. Missing hardware features will be silently ignored.
This module supports multiple devices connected to I²C bus. The devices of different types can be mixed.
The addressing of ADS family allows for maximum of 4 devices connected to the same I²C bus.
!!! caution !!! caution
The **ABSOLUTE MAXIMUM RATINGS** for all analog inputs are `–0.3V to VDD+0.3V` referred to GND. The **ABSOLUTE MAXIMUM RATINGS** for all analog inputs are `–0.3V to VDD+0.3V` referred to GND.
## ads1115.read()
## ads1115.ads1115()
Registers ADS1115 (ADS1113, ADS1114) device.
#### Syntax
`ads1115.ADS1115(I2C_ID, I2C_ADDR)`
#### Parameters
- `I2C_ID` - always 0
- `ADDRESS` - I²C address of a device
* `ads1115.ADDR_GND`
* `ads1115.ADDR_VDD`
* `ads1115.ADDR_SDA`
* `ads1115.ADDR_SCL`
#### Returns
Registered `device` object
#### Example
```lua
local id, sda, scl = 0, 6, 5
i2c.setup(id, sda, scl, i2c.SLOW)
ads1115.reset()
adc1 = ads1115.ads1115(id, ads1115.ADDR_GND)
```
## ads1115.ads1015()
Registers ADS1015 (ADS1013, ADS1014) device.
#### Syntax
`ads1115.ads1015(I2C_ID, I2C_ADDR)`
#### Parameters
- `I2C_ID` - always 0
- `ADDRESS` - I²C address of a device
* `ads1115.ADDR_GND`
* `ads1115.ADDR_VDD`
* `ads1115.ADDR_SDA`
* `ads1115.ADDR_SCL`
#### Returns
Registered `device` object
#### Example
```lua
local id, sda, scl = 0, 6, 5
i2c.setup(id, sda, scl, i2c.SLOW)
ads1115.reset()
adc1 = ads1115.ads1015(id, ads1115.ADDR_VDD)
adc2 = ads1115.ads1115(id, ads1115.ADDR_SDA)
```
## ads1115.reset()
Reset all devices connected to I²C interface.
### Syntax
ads1115.reset()
#### Parameters
none
#### Returns
`nil`
#### Example
```lua
local id, alert_pin, sda, scl = 0, 7, 6, 5
i2c.setup(id, sda, scl, i2c.SLOW)
ads1115.reset()
```
# ADS Device
## ads1115.device:read()
Gets the result stored in the register of a previously issued conversion, e.g. in continuous mode or with a conversion ready interrupt. Gets the result stored in the register of a previously issued conversion, e.g. in continuous mode or with a conversion ready interrupt.
#### Syntax #### Syntax
`volt, volt_dec, adc = ads1115.read()` `volt, volt_dec, raw, sign = device:read()`
#### Parameters #### Parameters
none none
#### Returns #### Returns
- `volt` voltage in mV (see note below) - `volt` voltage in mV (see note below)
- `volt_dec` voltage decimal (see note below) - `volt_dec` voltage decimal in uV (see note below)
- `adc` raw adc value - `adc` raw adc register value
- `sign` sign of the result (see note below)
!!! note !!! note
If using float firmware then `volt` is a floating point number. On an integer firmware, the final value has to be concatenated from `volt` and `volt_dec`. If using float firmware then `volt` is a floating point number, `volt_dec` and `sign` are nil. On an integer firmware, the final value has to be concatenated from `volt`, `volt_dec` and `sign`. On integer firmware `volt` and `volt_dec` are always positive, sign can be `-1`, `0`, `1`.
#### Example #### Example
```lua ```lua
local id, alert_pin, sda, scl = 0, 7, 6, 5 local id, alert_pin, sda, scl = 0, 7, 6, 5
i2c.setup(id, sda, scl, i2c.SLOW) i2c.setup(id, sda, scl, i2c.SLOW)
ads1115.setup(ads1115.ADDR_GND) ads1115.reset()
adc1 = ads1115.ads1115(id, ads1115.ADDR_GND)
-- continuous mode -- continuous mode
ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS) adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS)
-- read adc result with read() -- read adc result with read()
volt, volt_dec, adc = ads1115.read() volt, volt_dec, adc, sign = ads1:read()
print(volt, volt_dec, adc) print(volt, volt_dec, adc, sign)
-- comparator -- comparator
ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS, ads1115.COMP_1CONV, 1000, 2000) adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.CONTINUOUS, ads1115.COMP_1CONV, 1000, 2000)
local function comparator(level, when) local function comparator(level, when)
-- read adc result with read() when threshold reached -- read adc result with read() when threshold reached
volt, volt_dec, adc = ads1115.read() gpio.trig(alert_pin)
print(volt, volt_dec, adc) volt, volt_dec, adc, sign = ads1:read()
print(volt, volt_dec, adc, sign)
end end
gpio.mode(alert_pin, gpio.INT) gpio.mode(alert_pin, gpio.INT)
gpio.trig(alert_pin, "both", comparator) gpio.trig(alert_pin, "both", comparator)
-- read adc result with read() -- read adc result with read()
volt, volt_dec, adc = ads1115.read() volt, volt_dec, adc, sign = ads1115:read()
print(volt, volt_dec, adc) print(volt, volt_dec, adc, sing)
-- format value in int build
if sign then
-- int build
print(string.format("%s%d.%03d mV", sign >= 0 and "+" or "-", volt, volt_dec))
else
-- float build
-- just use V as it is
end
``` ```
## ads1115.setting()
## ads1115.device:setting()
Configuration settings for the ADC. Configuration settings for the ADC.
#### Syntax #### Syntax
`ads1115.setting(GAIN, SAMPLES, CHANNEL, MODE[, CONVERSION_RDY][, COMPARATOR, THRESHOLD_LOW, THRESHOLD_HI])` `device:setting(GAIN, SAMPLES, CHANNEL, MODE[, CONVERSION_RDY][, COMPARATOR, THRESHOLD_LOW, THRESHOLD_HI[,COMP_MODE]])`
#### Parameters #### Parameters
- `GAIN` Programmable gain amplifier - `GAIN` Programmable gain amplifier
...@@ -68,14 +161,19 @@ Configuration settings for the ADC. ...@@ -68,14 +161,19 @@ Configuration settings for the ADC.
* `ads1115.GAIN_0_512V` 8x Gain * `ads1115.GAIN_0_512V` 8x Gain
* `ads1115.GAIN_0_256V` 16x Gain * `ads1115.GAIN_0_256V` 16x Gain
- `SAMPLES` Data rate in samples per second - `SAMPLES` Data rate in samples per second
* `ads1115.DR_8SPS` * `ads1115.DR_8SPS` ADS1115 only
* `ads1115.DR_16SPS` * `ads1115.DR_16SPS` ADS1115 only
* `ads1115.DR_32SPS` * `ads1115.DR_32SPS` ADS1115 only
* `ads1115.DR_64SPS` * `ads1115.DR_64SPS` ADS1115 only
* `ads1115.DR_128SPS` * `ads1115.DR_128SPS`
* `ads1115.DR_250SPS` * `ads1115.DR_250SPS`
* `ads1115.DR_475SPS` * `ads1115.DR_475SPS` ADS1115 only
* `ads1115.DR_860SPS` * `ads1115.DR_490SPS` ADS1015 only
* `ads1115.DR_860SPS` ADS1115 only
* `ads1115.DR_920SPS` ADS1015 only
* `ads1115.DR_1600SPS` ADS1015 only
* `ads1115.DR_2400SPS` ADS1015 only
* `ads1115.DR_3300SPS` ADS1015 only
- `CHANNEL` Input multiplexer for single-ended or differential measurement - `CHANNEL` Input multiplexer for single-ended or differential measurement
* `ads1115.SINGLE_0` channel 0 to GND * `ads1115.SINGLE_0` channel 0 to GND
* `ads1115.SINGLE_1` channel 1 to GND * `ads1115.SINGLE_1` channel 1 to GND
...@@ -102,31 +200,11 @@ Configuration settings for the ADC. ...@@ -102,31 +200,11 @@ Configuration settings for the ADC.
- `THRESHOLD_HI` - `THRESHOLD_HI`
* `0` - `+ GAIN_MAX` in mV for single-ended inputs * `0` - `+ GAIN_MAX` in mV for single-ended inputs
* `- GAIN_MAX` - `+ GAIN_MAX` in mV for differential inputs * `- GAIN_MAX` - `+ GAIN_MAX` in mV for differential inputs
- `COMP_MODE` Comparator mode
* `ads1115.CMODE_TRAD` traditional comparator mode (with hysteresis)
* `ads1115.CMODE_WINDOW` window comparator mode
#### Returns note: Comparator and conversion ready are always configured to non-latching, active low.
`nil`
#### Example
```lua
local id, sda, scl = 0, 6, 5
i2c.setup(id, sda, scl, i2c.SLOW)
ads1115.setup(ads1115.ADDR_GND)
ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT)
```
## ads1115.setup()
Initializes the device on the defined I²C device address.
#### Syntax
`ads1115.setup(ADDRESS)`
#### Parameters
- `ADDRESS`
* `ads1115.ADDR_GND`
* `ads1115.ADDR_VDD`
* `ads1115.ADDR_SDA`
* `ads1115.ADDR_SCL`
#### Returns #### Returns
`nil` `nil`
...@@ -135,19 +213,22 @@ Initializes the device on the defined I²C device address. ...@@ -135,19 +213,22 @@ Initializes the device on the defined I²C device address.
```lua ```lua
local id, sda, scl = 0, 6, 5 local id, sda, scl = 0, 6, 5
i2c.setup(id, sda, scl, i2c.SLOW) i2c.setup(id, sda, scl, i2c.SLOW)
ads1115.reset()
adc1 = ads1115.ads1015(id, ads1115.ADDR_GND)
ads1115.setup(ads1115.ADDR_GND) adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_3300SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT)
``` ```
## ads1115.startread()
## ads1115.device:startread()
Starts the ADC reading for single-shot mode and after the conversion is done it will invoke an optional callback function in which the ADC conversion result can be obtained. Starts the ADC reading for single-shot mode and after the conversion is done it will invoke an optional callback function in which the ADC conversion result can be obtained.
#### Syntax #### Syntax
`ads1115.startread([CALLBACK])` `device:startread([CALLBACK])`
#### Parameters #### Parameters
- `CALLBACK` callback function which will be invoked after the adc conversion is done - `CALLBACK` callback function which will be invoked after the adc conversion is done
* `function(volt, volt_dec, adc) end` * `function(volt, volt_dec, adc, sign) end`
#### Returns #### Returns
- `nil` - `nil`
...@@ -156,21 +237,23 @@ Starts the ADC reading for single-shot mode and after the conversion is done it ...@@ -156,21 +237,23 @@ Starts the ADC reading for single-shot mode and after the conversion is done it
```lua ```lua
local id, alert_pin, sda, scl = 0, 7, 6, 5 local id, alert_pin, sda, scl = 0, 7, 6, 5
i2c.setup(id, sda, scl, i2c.SLOW) i2c.setup(id, sda, scl, i2c.SLOW)
ads1115.setup(ads1115.ADDR_GND) ads1115.reset()
adc1 = ads1115.ads1115(id, ads1115.ADDR_VDD)
-- single shot -- single shot
ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT) adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT)
-- start adc conversion and get result in callback after conversion is ready -- start adc conversion and get result in callback after conversion is ready
ads1115.startread(function(volt, volt_dec, adc) print(volt, volt_dec, adc) end) adc1:startread(function(volt, volt_dec, adc, sign) print(volt, volt_dec, adc, sign) end)
-- conversion ready -- conversion ready
ads1115.setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT, ads1115.CONV_RDY_1) adc1:setting(ads1115.GAIN_6_144V, ads1115.DR_128SPS, ads1115.SINGLE_0, ads1115.SINGLE_SHOT, ads1115.CONV_RDY_1)
local function conversion_ready(level, when) local function conversion_ready(level, when)
volt, volt_dec, adc = ads1115.read() gpio.trig(alert_pin)
print(volt, volt_dec, adc) volt, volt_dec, adc, sign = adc1:read()
print(volt, volt_dec, adc, sign)
end end
gpio.mode(alert_pin, gpio.INT) gpio.mode(alert_pin, gpio.INT)
gpio.trig(alert_pin, "down", conversion_ready) gpio.trig(alert_pin, "down", conversion_ready)
-- start conversion and get result with read() after conversion ready pin asserts -- start conversion and get result with read() after conversion ready pin asserts
ads1115.startread() adc1:startread()
``` ```
...@@ -24,23 +24,6 @@ local x,y,z = adxl345.read() ...@@ -24,23 +24,6 @@ local x,y,z = adxl345.read()
print(string.format("X = %d, Y = %d, Z = %d", x, y, z)) print(string.format("X = %d, Y = %d, Z = %d", x, y, z))
``` ```
## adxl345.init()
Initializes the module and sets the pin configuration.
!!! attention
This function is deprecated and will be removed in upcoming releases. Use `adxl345.setup()` instead.
#### Syntax
`adxl345.init(sda, scl)`
#### Parameters
- `sda` data pin
- `scl` clock pin
#### Returns
`nil`
## adxl345.setup() ## adxl345.setup()
Initializes the module. Initializes the module.
......
...@@ -6,27 +6,6 @@ ...@@ -6,27 +6,6 @@
This module provides access to the [AM2320](https://akizukidenshi.com/download/ds/aosong/AM2320.pdf) humidity and temperature sensor, using the i2c interface. This module provides access to the [AM2320](https://akizukidenshi.com/download/ds/aosong/AM2320.pdf) humidity and temperature sensor, using the i2c interface.
## am2320.init()
Initializes the module and sets the pin configuration. Returns model, version, serial but is seams these where all zero on my model.
!!! attention
This function is deprecated and will be removed in upcoming releases. Use `am2320.setup()` instead.
#### Syntax
`model, version, serial = am2320.init(sda, scl)`
#### Parameters
- `sda` data pin
- `scl` clock pin
#### Returns
- `model` 16 bits number of model
- `version` 8 bits version number
- `serial` 32 bits serial number
Note: I have only observed values of 0 for all of these, maybe other sensors return more sensible readings.
## am2320.read() ## am2320.read()
Samples the sensor and returns the relative humidity in % and temperature in celsius, as an integer multiplied with 10. Samples the sensor and returns the relative humidity in % and temperature in celsius, as an integer multiplied with 10.
......
...@@ -9,166 +9,166 @@ Bit manipulation support, on 32bit integers. ...@@ -9,166 +9,166 @@ Bit manipulation support, on 32bit integers.
## bit.arshift() ## bit.arshift()
Arithmetic right shift a number equivalent to `value >> shift` in C. Arithmetic right shift a number equivalent to `value >> shift` in C.
####Syntax #### Syntax
`bit.arshift(value, shift)` `bit.arshift(value, shift)`
####Parameters #### Parameters
- `value` the value to shift - `value` the value to shift
- `shift` positions to shift - `shift` positions to shift
####Returns #### Returns
the number shifted right (arithmetically) the number shifted right (arithmetically)
## bit.band() ## bit.band()
Bitwise AND, equivalent to `val1 & val2 & ... & valn` in C. Bitwise AND, equivalent to `val1 & val2 & ... & valn` in C.
####Syntax #### Syntax
`bit.band(val1, val2 [, ... valn])` `bit.band(val1, val2 [, ... valn])`
####Parameters #### Parameters
- `val1` first AND argument - `val1` first AND argument
- `val2` second AND argument - `val2` second AND argument
- `...valn` ...nth AND argument - `...valn` ...nth AND argument
####Returns #### Returns
the bitwise AND of all the arguments (number) the bitwise AND of all the arguments (number)
## bit.bit() ## bit.bit()
Generate a number with a 1 bit (used for mask generation). Equivalent to `1 << position` in C. Generate a number with a 1 bit (used for mask generation). Equivalent to `1 << position` in C.
####Syntax #### Syntax
`bit.bit(position)` `bit.bit(position)`
####Parameters #### Parameters
`position` position of the bit that will be set to 1 `position` position of the bit that will be set to 1
####Returns #### Returns
a number with only one 1 bit at position (the rest are set to 0) a number with only one 1 bit at position (the rest are set to 0)
## bit.bnot() ## bit.bnot()
Bitwise negation, equivalent to `~value in C. Bitwise negation, equivalent to `~value in C.`
####Syntax #### Syntax
`bit.bnot(value)` `bit.bnot(value)`
####Parameters #### Parameters
`value` the number to negate `value` the number to negate
####Returns #### Returns
the bitwise negated value of the number the bitwise negated value of the number
## bit.bor() ## bit.bor()
Bitwise OR, equivalent to `val1 | val2 | ... | valn` in C. Bitwise OR, equivalent to `val1 | val2 | ... | valn` in C.
####Syntax #### Syntax
`bit.bor(val1, val2 [, ... valn])` `bit.bor(val1, val2 [, ... valn])`
####Parameters #### Parameters
- `val1` first OR argument. - `val1` first OR argument.
- `val2` second OR argument. - `val2` second OR argument.
- `...valn` ...nth OR argument - `...valn` ...nth OR argument
####Returns #### Returns
the bitwise OR of all the arguments (number) the bitwise OR of all the arguments (number)
## bit.bxor() ## bit.bxor()
Bitwise XOR, equivalent to `val1 ^ val2 ^ ... ^ valn` in C. Bitwise XOR, equivalent to `val1 ^ val2 ^ ... ^ valn` in C.
####Syntax #### Syntax
`bit.bxor(val1, val2 [, ... valn])` `bit.bxor(val1, val2 [, ... valn])`
####Parameters #### Parameters
- `val1` first XOR argument - `val1` first XOR argument
- `val2` second XOR argument - `val2` second XOR argument
- `...valn` ...nth XOR argument - `...valn` ...nth XOR argument
####Returns #### Returns
the bitwise XOR of all the arguments (number) the bitwise XOR of all the arguments (number)
## bit.clear() ## bit.clear()
Clear bits in a number. Clear bits in a number.
####Syntax #### Syntax
`bit.clear(value, pos1 [, ... posn])` `bit.clear(value, pos1 [, ... posn])`
####Parameters #### Parameters
- `value` the base number - `value` the base number
- `pos1` position of the first bit to clear - `pos1` position of the first bit to clear
- `...posn` position of thet nth bit to clear - `...posn` position of thet nth bit to clear
####Returns #### Returns
the number with the bit(s) cleared in the given position(s) the number with the bit(s) cleared in the given position(s)
## bit.isclear() ## bit.isclear()
Test if a given bit is cleared. Test if a given bit is cleared.
####Syntax #### Syntax
`bit.isclear(value, position)` `bit.isclear(value, position)`
####Parameters #### Parameters
- `value` the value to test - `value` the value to test
- `position` bit position to test - `position` bit position to test
####Returns #### Returns
true if the bit at the given position is 0, false othewise true if the bit at the given position is 0, false othewise
## bit.isset() ## bit.isset()
Test if a given bit is set. Test if a given bit is set.
####Syntax #### Syntax
`bit.isset(value, position)` `bit.isset(value, position)`
####Parameters #### Parameters
- `value` the value to test - `value` the value to test
- `position` bit position to test - `position` bit position to test
####Returns #### Returns
true if the bit at the given position is 1, false otherwise true if the bit at the given position is 1, false otherwise
## bit.lshift() ## bit.lshift()
Left-shift a number, equivalent to `value << shift` in C. Left-shift a number, equivalent to `value << shift` in C.
####Syntax #### Syntax
`bit.lshift(value, shift)` `bit.lshift(value, shift)`
####Parameters #### Parameters
- `value` the value to shift - `value` the value to shift
- `shift` positions to shift - `shift` positions to shift
####Returns #### Returns
the number shifted left the number shifted left
## bit.rshift() ## bit.rshift()
Logical right shift a number, equivalent to `( unsigned )value >> shift` in C. Logical right shift a number, equivalent to `( unsigned )value >> shift` in C.
####Syntax #### Syntax
`bit.rshift(value, shift)` `bit.rshift(value, shift)`
####Parameters #### Parameters
- `value` the value to shift. - `value` the value to shift.
- `shift` positions to shift. - `shift` positions to shift.
####Returns #### Returns
the number shifted right (logically) the number shifted right (logically)
## bit.set() ## bit.set()
Set bits in a number. Set bits in a number.
####Syntax #### Syntax
`bit.set(value, pos1 [, ... posn ])` `bit.set(value, pos1 [, ... posn ])`
####Parameters #### Parameters
- `value` the base number. - `value` the base number.
- `pos1` position of the first bit to set. - `pos1` position of the first bit to set.
- `...posn` position of the nth bit to set. - `...posn` position of the nth bit to set.
####Returns #### Returns
the number with the bit(s) set in the given position(s) the number with the bit(s) set in the given position(s)
\ No newline at end of file
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