Merge branch 'dev' into dev-LFS
This commit is contained in:
commit
6db7414270
72
Makefile
72
Makefile
|
@ -2,24 +2,26 @@
|
|||
#
|
||||
.NOTPARALLEL:
|
||||
|
||||
# SDK version NodeMCU is locked to
|
||||
SDK_VER:=2.1.0
|
||||
# SDK base version, as released by Espressif
|
||||
SDK_BASE_VER:=2.2.0
|
||||
|
||||
# no patch: SDK_BASE_VER equals SDK_VER and sdk dir depends on sdk_extracted
|
||||
SDK_BASE_VER:=$(SDK_VER)
|
||||
SDK_DIR_DEPENDS:=sdk_extracted
|
||||
# with patch: SDK_BASE_VER differs from SDK_VER and sdk dir depends on sdk_patched
|
||||
#SDK_BASE_VER:=1.5.4
|
||||
#SDK_DIR_DEPENDS:=sdk_patched
|
||||
# no patch: SDK_VER equals SDK_BASE_VER and sdk dir depends on sdk_extracted
|
||||
#SDK_VER:=$(SDK_BASE_VER)
|
||||
#SDK_DIR_DEPENDS:=sdk_extracted
|
||||
|
||||
# with patch: SDK_VER differs from SDK_BASE_VER and sdk dir depends on sdk_patched
|
||||
SDK_PATCH_VER:=f8f27ce
|
||||
SDK_VER:=$(SDK_BASE_VER)-$(SDK_PATCH_VER)
|
||||
SDK_DIR_DEPENDS:=sdk_patched
|
||||
|
||||
SDK_FILE_VER:=$(SDK_BASE_VER)
|
||||
SDK_FILE_SHA1:=66a4272894dc1bcec19f5f8bf79fee80f60a021b
|
||||
#SDK_PATCH_VER:=$(SDK_VER)_patch_20160704
|
||||
#SDK_PATCH_SHA1:=388d9e91df74e3b49fca126da482cf822cf1ebf1
|
||||
SDK_FILE_SHA1:=8b63f1066d3560ff77f119e8ba30a9c39e7baaad
|
||||
SDK_PATCH_SHA1:=0bc21ec77b08488f04d3e1c9d161b711d07201a8
|
||||
# Ensure we search "our" SDK before the tool-chain's SDK (if any)
|
||||
TOP_DIR:=$(abspath $(dir $(lastword $(MAKEFILE_LIST))))
|
||||
SDK_DIR:=$(TOP_DIR)/sdk/esp_iot_sdk_v$(SDK_VER)
|
||||
CCFLAGS:= -I$(TOP_DIR)/sdk-overrides/include -I$(SDK_DIR)/include
|
||||
SDK_REL_DIR=sdk/esp_iot_sdk_v$(SDK_VER)
|
||||
SDK_DIR:=$(TOP_DIR)/$(SDK_REL_DIR)
|
||||
CCFLAGS:= -I$(TOP_DIR)/sdk-overrides/include -I$(TOP_DIR)/app/include/lwip/app -I$(SDK_DIR)/include
|
||||
LDFLAGS:= -L$(SDK_DIR)/lib -L$(SDK_DIR)/ld $(LDFLAGS)
|
||||
|
||||
ifdef DEBUG
|
||||
|
@ -39,6 +41,7 @@ ifeq ($(OS),Windows_NT)
|
|||
# It is xcc
|
||||
AR = xt-ar
|
||||
CC = xt-xcc
|
||||
CXX = xt-xcc
|
||||
NM = xt-nm
|
||||
CPP = xt-cpp
|
||||
OBJCOPY = xt-objcopy
|
||||
|
@ -50,6 +53,7 @@ ifeq ($(OS),Windows_NT)
|
|||
CCFLAGS += -ffunction-sections -fno-jump-tables -fdata-sections
|
||||
AR = xtensa-lx106-elf-ar
|
||||
CC = xtensa-lx106-elf-gcc
|
||||
CXX = xtensa-lx106-elf-g++
|
||||
NM = xtensa-lx106-elf-nm
|
||||
CPP = xtensa-lx106-elf-cpp
|
||||
OBJCOPY = xtensa-lx106-elf-objcopy
|
||||
|
@ -77,6 +81,7 @@ else
|
|||
CCFLAGS += -ffunction-sections -fno-jump-tables -fdata-sections
|
||||
AR = xtensa-lx106-elf-ar
|
||||
CC = $(WRAPCC) xtensa-lx106-elf-gcc
|
||||
CXX = $(WRAPCC) xtensa-lx106-elf-g++
|
||||
NM = xtensa-lx106-elf-nm
|
||||
CPP = $(WRAPCC) xtensa-lx106-elf-gcc -E
|
||||
OBJCOPY = xtensa-lx106-elf-objcopy
|
||||
|
@ -104,6 +109,7 @@ ESPTOOL ?= ../tools/esptool.py
|
|||
|
||||
|
||||
CSRCS ?= $(wildcard *.c)
|
||||
CXXSRCS ?= $(wildcard *.cpp)
|
||||
ASRCs ?= $(wildcard *.s)
|
||||
ASRCS ?= $(wildcard *.S)
|
||||
SUBDIRS ?= $(patsubst %/,%,$(dir $(filter-out tools/Makefile,$(wildcard */Makefile))))
|
||||
|
@ -112,10 +118,12 @@ ODIR := .output
|
|||
OBJODIR := $(ODIR)/$(TARGET)/$(FLAVOR)/obj
|
||||
|
||||
OBJS := $(CSRCS:%.c=$(OBJODIR)/%.o) \
|
||||
$(CXXSRCS:%.cpp=$(OBJODIR)/%.o) \
|
||||
$(ASRCs:%.s=$(OBJODIR)/%.o) \
|
||||
$(ASRCS:%.S=$(OBJODIR)/%.o)
|
||||
|
||||
DEPS := $(CSRCS:%.c=$(OBJODIR)/%.d) \
|
||||
$(CXXSCRS:%.cpp=$(OBJODIR)/%.d) \
|
||||
$(ASRCs:%.s=$(OBJODIR)/%.d) \
|
||||
$(ASRCS:%.S=$(OBJODIR)/%.d)
|
||||
|
||||
|
@ -196,26 +204,31 @@ $(BINODIR)/%.bin: $(IMAGEODIR)/%.out
|
|||
# Should be done in top-level makefile only
|
||||
#
|
||||
|
||||
all: $(SDK_DIR_DEPENDS) pre_build .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
|
||||
all: sdk_pruned pre_build .subdirs $(OBJS) $(OLIBS) $(OIMAGES) $(OBINS) $(SPECIAL_MKTARGETS)
|
||||
|
||||
.PHONY: sdk_extracted
|
||||
.PHONY: sdk_patched
|
||||
.PHONY: sdk_pruned
|
||||
|
||||
sdk_extracted: $(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER)
|
||||
sdk_patched: sdk_extracted $(TOP_DIR)/sdk/.patched-$(SDK_VER)
|
||||
sdk_pruned: $(SDK_DIR_DEPENDS) $(TOP_DIR)/sdk/.pruned-$(SDK_VER)
|
||||
|
||||
$(TOP_DIR)/sdk/.extracted-$(SDK_BASE_VER): $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip
|
||||
mkdir -p "$(dir $@)"
|
||||
(cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK-$(SDK_VER) && unzip $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK-$(SDK_VER)/lib/* ESP8266_NONOS_SDK-$(SDK_VER)/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK-$(SDK_VER)/include/* ESP8266_NONOS_SDK-$(SDK_VER)/bin/esp_init_data_default.bin)
|
||||
mv $(dir $@)/ESP8266_NONOS_SDK-$(SDK_VER) $(dir $@)/esp_iot_sdk_v$(SDK_VER)
|
||||
rm -f $(SDK_DIR)/lib/liblwip.a
|
||||
(cd "$(dir $@)" && rm -fr esp_iot_sdk_v$(SDK_VER) ESP8266_NONOS_SDK-$(SDK_BASE_VER) && unzip $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip ESP8266_NONOS_SDK-$(SDK_BASE_VER)/lib/* ESP8266_NONOS_SDK-$(SDK_BASE_VER)/ld/eagle.rom.addr.v6.ld ESP8266_NONOS_SDK-$(SDK_BASE_VER)/include/* ESP8266_NONOS_SDK-$(SDK_BASE_VER)/bin/esp_init_data_default_v05.bin)
|
||||
mv $(dir $@)/ESP8266_NONOS_SDK-$(SDK_BASE_VER) $(dir $@)/esp_iot_sdk_v$(SDK_BASE_VER)
|
||||
touch $@
|
||||
|
||||
$(TOP_DIR)/sdk/.patched-$(SDK_VER): $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER).zip
|
||||
mkdir -p "$(dir $@)/patch"
|
||||
(cd "$(dir $@)/patch" && unzip $(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER)*.zip *.a esp_init_data_default.bin && mv *.a $(SDK_DIR)/lib/ && mv esp_init_data_default.bin $(SDK_DIR)/bin/)
|
||||
rmdir $(dir $@)/patch
|
||||
rm -f $(SDK_DIR)/lib/liblwip.a
|
||||
$(TOP_DIR)/sdk/.patched-$(SDK_VER): $(TOP_DIR)/cache/$(SDK_PATCH_VER).patch
|
||||
mv $(dir $@)/esp_iot_sdk_v$(SDK_BASE_VER) $(dir $@)/esp_iot_sdk_v$(SDK_VER)
|
||||
git apply --verbose -p1 --exclude='*VERSION' --exclude='*bin/at*' --directory=$(SDK_REL_DIR) $<
|
||||
touch $@
|
||||
|
||||
$(TOP_DIR)/sdk/.pruned-$(SDK_VER):
|
||||
rm -f $(SDK_DIR)/lib/liblwip.a $(SDK_DIR)/lib/libssl.a $(SDK_DIR)/lib/libmbedtls.a
|
||||
ar d $(SDK_DIR)/lib/libmain.a time.o
|
||||
ar d $(SDK_DIR)/lib/libc.a lib_a-time.o
|
||||
touch $@
|
||||
|
||||
$(TOP_DIR)/cache/v$(SDK_FILE_VER).zip:
|
||||
|
@ -223,9 +236,9 @@ $(TOP_DIR)/cache/v$(SDK_FILE_VER).zip:
|
|||
wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused https://github.com/espressif/ESP8266_NONOS_SDK/archive/v$(SDK_FILE_VER).zip -O $@ || { rm -f "$@"; exit 1; }
|
||||
(echo "$(SDK_FILE_SHA1) $@" | sha1sum -c -) || { rm -f "$@"; exit 1; }
|
||||
|
||||
$(TOP_DIR)/cache/esp_iot_sdk_v$(SDK_PATCH_VER).zip:
|
||||
$(TOP_DIR)/cache/$(SDK_PATCH_VER).patch:
|
||||
mkdir -p "$(dir $@)"
|
||||
wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused http://espressif.com/sites/default/files/sdks/esp8266_nonos_sdk_v$(SDK_PATCH_VER).zip -O $@ || { rm -f "$@"; exit 1; }
|
||||
wget --tries=10 --timeout=15 --waitretry=30 --read-timeout=20 --retry-connrefused "https://github.com/espressif/ESP8266_NONOS_SDK/compare/v$(SDK_BASE_VER)...$(SDK_PATCH_VER).patch" -O $@ || { rm -f "$@"; exit 1; }
|
||||
(echo "$(SDK_PATCH_SHA1) $@" | sha1sum -c -) || { rm -f "$@"; exit 1; }
|
||||
|
||||
clean:
|
||||
|
@ -306,6 +319,17 @@ $(OBJODIR)/%.d: %.c
|
|||
sed 's,\($*\.o\)[ :]*,$(OBJODIR)/\1 $@ : ,g' < $@.$$$$ > $@; \
|
||||
rm -f $@.$$$$
|
||||
|
||||
$(OBJODIR)/%.o: %.cpp
|
||||
@mkdir -p $(OBJODIR);
|
||||
$(CXX) $(if $(findstring $<,$(DSRCS)),$(DFLAGS),$(CFLAGS)) $(COPTS_$(*F)) -o $@ -c $<
|
||||
|
||||
$(OBJODIR)/%.d: %.cpp
|
||||
@mkdir -p $(OBJODIR);
|
||||
@echo DEPEND: $(CXX) -M $(CFLAGS) $<
|
||||
@set -e; rm -f $@; \
|
||||
sed 's,\($*\.o\)[ :]*,$(OBJODIR)/\1 $@ : ,g' < $@.$$$$ > $@; \
|
||||
rm -f $@.$$$$
|
||||
|
||||
$(OBJODIR)/%.o: %.s
|
||||
@mkdir -p $(OBJODIR);
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# **NodeMCU 2.1.0** #
|
||||
# **NodeMCU 2.2.0** #
|
||||
|
||||
[![Join the chat at https://gitter.im/nodemcu/nodemcu-firmware](https://img.shields.io/gitter/room/badges/shields.svg)](https://gitter.im/nodemcu/nodemcu-firmware?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[![Build Status](https://travis-ci.org/nodemcu/nodemcu-firmware.svg)](https://travis-ci.org/nodemcu/nodemcu-firmware)
|
||||
|
@ -7,7 +7,7 @@
|
|||
|
||||
### A Lua based firmware for ESP8266 WiFi SOC
|
||||
|
||||
NodeMCU is an [eLua](http://www.eluaproject.net/) based firmware for the [ESP8266 WiFi SOC from Espressif](http://espressif.com/en/products/esp8266/). The firmware is based on the [Espressif NON-OS SDK 2.1.0](https://github.com/espressif/ESP8266_NONOS_SDK/releases/tag/v2.1.0) and uses a file system based on [spiffs](https://github.com/pellepl/spiffs). The code repository consists of 98.1% C-code that glues the thin Lua veneer to the SDK.
|
||||
NodeMCU is an [eLua](http://www.eluaproject.net/) based firmware for the [ESP8266 WiFi SOC from Espressif](http://espressif.com/en/products/esp8266/). The firmware is based on the [Espressif NON-OS SDK 2.2.0](https://github.com/espressif/ESP8266_NONOS_SDK/releases/tag/v2.2.0) and uses a file system based on [spiffs](https://github.com/pellepl/spiffs). The code repository consists of 98.1% C-code that glues the thin Lua veneer to the SDK.
|
||||
|
||||
The NodeMCU *firmware* is a companion project to the popular [NodeMCU dev kits](https://github.com/nodemcu/nodemcu-devkit-v1.0), ready-made open source development boards with ESP8266-12E chips.
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ COMPONENTS_eagle.app.v6 = \
|
|||
misc/libmisc.a \
|
||||
$(OPT_SEL_COMPONENTS)
|
||||
|
||||
|
||||
# Inspect the modules library and work out which modules need to be linked.
|
||||
# For each enabled module, a symbol name of the form XYZ_module_selected is
|
||||
# returned. At link time those names are declared undefined, so those (and
|
||||
|
@ -140,6 +141,7 @@ CONFIGURATION_DEFINES = -D__ets__ \
|
|||
-DLWIP_OPEN_SRC \
|
||||
-DPBUF_RSV_FOR_WLAN \
|
||||
-DEBUF_LWIP \
|
||||
-DUSE_OPTIMIZE_PRINTF \
|
||||
-DMBEDTLS_USER_CONFIG_FILE=\"user_mbedtls.h\" \
|
||||
|
||||
DEFINES += \
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "node.h"
|
||||
#include "coap_timer.h"
|
||||
#include "os_type.h"
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
static os_timer_t coap_timer;
|
||||
static coap_tick_t basetime = 0;
|
||||
|
@ -48,6 +49,8 @@ void coap_timer_tick(void *arg){
|
|||
void coap_timer_setup(coap_queue_t ** queue, coap_tick_t t){
|
||||
os_timer_disarm(&coap_timer);
|
||||
os_timer_setfn(&coap_timer, (os_timer_func_t *)coap_timer_tick, queue);
|
||||
SWTIMER_REG_CB(coap_timer_tick, SWTIMER_RESUME);
|
||||
//coap_timer_tick processes a queue, my guess is that it is ok to resume the timer from where it left off
|
||||
os_timer_arm(&coap_timer, t, 0); // no repeat
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mem.h"
|
||||
#include "gpio.h"
|
||||
#include "user_interface.h"
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
#include "driver/key.h"
|
||||
|
||||
|
@ -132,9 +133,9 @@ key_50ms_cb(struct single_key_param *single_key)
|
|||
LOCAL void
|
||||
key_intr_handler(void *arg)
|
||||
{
|
||||
struct keys_param *keys = arg;
|
||||
uint8 i;
|
||||
uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
|
||||
struct keys_param *keys = arg;
|
||||
|
||||
for (i = 0; i < keys->key_num; i++) {
|
||||
if (gpio_status & BIT(keys->single_key[i]->gpio_id)) {
|
||||
|
@ -148,6 +149,8 @@ key_intr_handler(void *arg)
|
|||
// 5s, restart & enter softap mode
|
||||
os_timer_disarm(&keys->single_key[i]->key_5s);
|
||||
os_timer_setfn(&keys->single_key[i]->key_5s, (os_timer_func_t *)key_5s_cb, keys->single_key[i]);
|
||||
SWTIMER_REG_CB(key_5s_cb, SWTIMER_DROP);
|
||||
// key_5s_cb checks the state of a gpio. After resume, gpio state would be invalid
|
||||
os_timer_arm(&keys->single_key[i]->key_5s, 5000, 0);
|
||||
keys->single_key[i]->key_level = 0;
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(keys->single_key[i]->gpio_id), GPIO_PIN_INTR_POSEDGE);
|
||||
|
@ -155,6 +158,8 @@ key_intr_handler(void *arg)
|
|||
// 50ms, check if this is a real key up
|
||||
os_timer_disarm(&keys->single_key[i]->key_50ms);
|
||||
os_timer_setfn(&keys->single_key[i]->key_50ms, (os_timer_func_t *)key_50ms_cb, keys->single_key[i]);
|
||||
SWTIMER_REG_CB(key_50ms_cb, SWTIMER_DROP);
|
||||
// key_50ms_cb checks the state of a gpio. After resume, gpio state would be invalid
|
||||
os_timer_arm(&keys->single_key[i]->key_50ms, 50, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -527,6 +527,7 @@ void spi_slave_init(uint8 spi_no)
|
|||
|
||||
|
||||
#ifdef SPI_SLAVE_DEBUG
|
||||
#include "pm/swtimer.h"
|
||||
/******************************************************************************
|
||||
* FunctionName : hspi_master_readwrite_repeat
|
||||
* Description : SPI master test function for reading and writing esp8266 slave buffer,
|
||||
|
@ -545,6 +546,8 @@ void hspi_master_readwrite_repeat(void)
|
|||
temp++;
|
||||
spi_byte_write_espslave(SPI_HSPI,temp);
|
||||
os_timer_setfn(&timer2, (os_timer_func_t *)hspi_master_readwrite_repeat, NULL);
|
||||
SWTIMER_REGISTER_CB_PTR(hspi_master_readwrite_repeat, SWTIMER_RESUME);
|
||||
//hspi_master_readwrite_repeat timer will be resumed on wake up, maybe data will still be in buffer?
|
||||
os_timer_arm(&timer2, 500, 0);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -323,11 +323,14 @@ uart_autobaud_timeout(void *timer_arg)
|
|||
uart_div_modify(uart_no, divisor);
|
||||
}
|
||||
}
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
static void
|
||||
uart_init_autobaud(uint32_t uart_no)
|
||||
{
|
||||
os_timer_setfn(&autobaud_timer, uart_autobaud_timeout, (void *) uart_no);
|
||||
SWTIMER_REG_CB(uart_autobaud_timeout, SWTIMER_DROP);
|
||||
//if autobaud hasn't done it's thing by the time light sleep triggered, it probably isn't going to happen.
|
||||
os_timer_arm(&autobaud_timer, 100, TRUE);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "limits.h"
|
||||
#include "httpclient.h"
|
||||
#include "stdlib.h"
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
#define REDIRECTION_FOLLOW_MAX 20
|
||||
|
||||
|
@ -525,6 +526,8 @@ static void ICACHE_FLASH_ATTR http_dns_callback( const char * hostname, ip_addr_
|
|||
/* Set connection timeout timer */
|
||||
os_timer_disarm( &(req->timeout_timer) );
|
||||
os_timer_setfn( &(req->timeout_timer), (os_timer_func_t *) http_timeout_callback, conn );
|
||||
SWTIMER_REG_CB(http_timeout_callback, SWTIMER_IMMEDIATE);
|
||||
//http_timeout_callback frees memory used by this function and timer cannot be dropped
|
||||
os_timer_arm( &(req->timeout_timer), req->timeout, false );
|
||||
|
||||
#ifdef CLIENT_SSL_ENABLE
|
||||
|
|
|
@ -65,7 +65,7 @@ typedef uint32_t mem_ptr_t;
|
|||
#define U32_F "d"
|
||||
#define X32_F "x"
|
||||
|
||||
|
||||
#define LWIP_ERR_T s32_t
|
||||
|
||||
//#define PACK_STRUCT_FIELD(x) x __attribute__((packed))
|
||||
#define PACK_STRUCT_FIELD(x) x
|
||||
|
|
|
@ -1,6 +1,31 @@
|
|||
/*
|
||||
* ESPRESSIF MIT License
|
||||
*
|
||||
* Copyright (c) 2016 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
|
||||
*
|
||||
* Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case,
|
||||
* it is free of charge, to any person obtaining a copy of this software and associated
|
||||
* documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ESPCONN_H__
|
||||
#define __ESPCONN_H__
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "os_type.h"
|
||||
#include "lwip/app/espconn_buf.h"
|
||||
|
@ -37,8 +62,7 @@ typedef void (* espconn_reconnect_callback)(void *arg, sint8 err);
|
|||
#define ESPCONN_NODATA -17 /* No data can be read */
|
||||
|
||||
#define ESPCONN_HANDSHAKE -28 /* ssl handshake failed */
|
||||
#define ESPCONN_RESP_TIMEOUT -29 /* ssl handshake no response*/
|
||||
#define ESPCONN_PROTO_MSG -61 /* ssl application invalid */
|
||||
#define ESPCONN_SSL_INVALID_DATA -61 /* ssl application invalid */
|
||||
|
||||
#define ESPCONN_SSL 0x01
|
||||
#define ESPCONN_NORM 0x00
|
||||
|
@ -121,6 +145,7 @@ enum espconn_option{
|
|||
ESPCONN_NODELAY = 0x02,
|
||||
ESPCONN_COPY = 0x04,
|
||||
ESPCONN_KEEPALIVE = 0x08,
|
||||
ESPCONN_MANUALRECV = 0x10,
|
||||
ESPCONN_END
|
||||
};
|
||||
|
||||
|
@ -461,6 +486,17 @@ extern sint8 espconn_regist_sentcb(struct espconn *espconn, espconn_sent_callbac
|
|||
*******************************************************************************/
|
||||
extern sint8 espconn_regist_write_finish(struct espconn *espconn, espconn_connect_callback write_finish_fn);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_send
|
||||
* Description : sent data for client or server
|
||||
* Parameters : espconn -- espconn to set for client or server
|
||||
* psent -- data to send
|
||||
* length -- length of data to send
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
extern sint8 espconn_send(struct espconn *espconn, uint8 *psent, uint16 length);
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_sent
|
||||
* Description : sent data for client or server
|
||||
|
@ -573,7 +609,163 @@ extern sint8 espconn_get_keepalive(struct espconn *espconn, uint8 level, void *o
|
|||
* - ESPCONN_ARG: dns client not initialized or invalid hostname
|
||||
*******************************************************************************/
|
||||
|
||||
extern sint8 espconn_gethostbyname(struct espconn *pespconn, const char *name, ip_addr_t *addr, dns_found_callback found);
|
||||
extern err_t espconn_gethostbyname(struct espconn *pespconn, const char *name, ip_addr_t *addr, dns_found_callback found);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_abort
|
||||
* Description : Forcely abort with host
|
||||
* Parameters : espconn -- the espconn used to connect with the host
|
||||
* Returns : result
|
||||
*******************************************************************************/
|
||||
|
||||
extern sint8 espconn_abort(struct espconn *espconn);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_encry_connect
|
||||
* Description : The function given as connection
|
||||
* Parameters : espconn -- the espconn used to connect with the host
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
|
||||
extern sint8 espconn_secure_connect(struct espconn *espconn);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_encry_disconnect
|
||||
* Description : The function given as the disconnection
|
||||
* Parameters : espconn -- the espconn used to disconnect with the host
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
|
||||
extern sint8 espconn_secure_disconnect(struct espconn *espconn);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_secure_send
|
||||
* Description : sent data for client or server
|
||||
* Parameters : espconn -- espconn to set for client or server
|
||||
* psent -- data to send
|
||||
* length -- length of data to send
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
|
||||
extern sint8 espconn_secure_send(struct espconn *espconn, uint8 *psent, uint16 length);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_encry_sent
|
||||
* Description : sent data for client or server
|
||||
* Parameters : espconn -- espconn to set for client or server
|
||||
* psent -- data to send
|
||||
* length -- length of data to send
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
|
||||
extern sint8 espconn_secure_sent(struct espconn *espconn, uint8 *psent, uint16 length);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_secure_set_size
|
||||
* Description : set the buffer size for client or server
|
||||
* Parameters : level -- set for client or server
|
||||
* 1: client,2:server,3:client and server
|
||||
* size -- buffer size
|
||||
* Returns : true or false
|
||||
*******************************************************************************/
|
||||
|
||||
extern bool espconn_secure_set_size(uint8 level, uint16 size);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_secure_get_size
|
||||
* Description : get buffer size for client or server
|
||||
* Parameters : level -- set for client or server
|
||||
* 1: client,2:server,3:client and server
|
||||
* Returns : buffer size for client or server
|
||||
*******************************************************************************/
|
||||
|
||||
extern sint16 espconn_secure_get_size(uint8 level);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_secure_ca_enable
|
||||
* Description : enable the certificate authenticate and set the flash sector
|
||||
* as client or server
|
||||
* Parameters : level -- set for client or server
|
||||
* 1: client,2:server,3:client and server
|
||||
* flash_sector -- flash sector for save certificate
|
||||
* Returns : result true or false
|
||||
*******************************************************************************/
|
||||
|
||||
extern bool espconn_secure_ca_enable(uint8 level, uint32 flash_sector );
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_secure_ca_disable
|
||||
* Description : disable the certificate authenticate as client or server
|
||||
* Parameters : level -- set for client or server
|
||||
* 1: client,2:server,3:client and server
|
||||
* Returns : result true or false
|
||||
*******************************************************************************/
|
||||
|
||||
extern bool espconn_secure_ca_disable(uint8 level);
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_secure_cert_req_enable
|
||||
* Description : enable the client certificate authenticate and set the flash sector
|
||||
* as client or server
|
||||
* Parameters : level -- set for client or server
|
||||
* 1: client,2:server,3:client and server
|
||||
* flash_sector -- flash sector for save certificate
|
||||
* Returns : result true or false
|
||||
*******************************************************************************/
|
||||
|
||||
extern bool espconn_secure_cert_req_enable(uint8 level, uint32 flash_sector );
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_secure_ca_disable
|
||||
* Description : disable the client certificate authenticate as client or server
|
||||
* Parameters : level -- set for client or server
|
||||
* 1: client,2:server,3:client and server
|
||||
* Returns : result true or false
|
||||
*******************************************************************************/
|
||||
|
||||
extern bool espconn_secure_cert_req_disable(uint8 level);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_secure_set_default_certificate
|
||||
* Description : Load the certificates in memory depending on compile-time
|
||||
* and user options.
|
||||
* Parameters : certificate -- Load the certificate
|
||||
* length -- Load the certificate length
|
||||
* Returns : result true or false
|
||||
*******************************************************************************/
|
||||
|
||||
extern bool espconn_secure_set_default_certificate(const uint8* certificate, uint16 length);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_secure_set_default_private_key
|
||||
* Description : Load the key in memory depending on compile-time
|
||||
* and user options.
|
||||
* Parameters : private_key -- Load the key
|
||||
* length -- Load the key length
|
||||
* Returns : result true or false
|
||||
*******************************************************************************/
|
||||
|
||||
extern bool espconn_secure_set_default_private_key(const uint8* private_key, uint16 length);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_secure_accept
|
||||
* Description : The function given as the listen
|
||||
* Parameters : espconn -- the espconn used to listen the connection
|
||||
* Returns : result
|
||||
*******************************************************************************/
|
||||
|
||||
extern sint8 espconn_secure_accept(struct espconn *espconn);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_secure_accepts
|
||||
* Description : delete the secure server host
|
||||
* Parameters : espconn -- the espconn used to listen the connection
|
||||
* Returns : result
|
||||
*******************************************************************************/
|
||||
|
||||
extern sint8 espconn_secure_delete(struct espconn *espconn);
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_igmp_join
|
||||
|
@ -593,6 +785,22 @@ extern sint8 espconn_igmp_join(ip_addr_t *host_ip, ip_addr_t *multicast_ip);
|
|||
*******************************************************************************/
|
||||
extern sint8 espconn_igmp_leave(ip_addr_t *host_ip, ip_addr_t *multicast_ip);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_recv_hold
|
||||
* Description : hold tcp receive
|
||||
* Parameters : espconn -- espconn to hold
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
extern sint8 espconn_recv_hold(struct espconn *pespconn);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_recv_unhold
|
||||
* Description : unhold tcp receive
|
||||
* Parameters : espconn -- espconn to unhold
|
||||
* Returns : none
|
||||
*******************************************************************************/
|
||||
extern sint8 espconn_recv_unhold(struct espconn *pespconn);
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_mdns_init
|
||||
* Description : register a device with mdns
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
#define espconn_keepalive_enable(pcb) ((pcb)->so_options |= SOF_KEEPALIVE)
|
||||
#define espconn_keepalive_disable(pcb) ((pcb)->so_options &= ~SOF_KEEPALIVE)
|
||||
|
||||
#define espconn_manual_recv_disabled(espconn) (((espconn)->pcommon.espconn_opt & ESPCONN_MANUALRECV) != 0)
|
||||
#define espconn_manual_recv_enabled(espconn) (((espconn)->pcommon.espconn_opt & ESPCONN_MANUALRECV) == 0)
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_kill_oldest_pcb
|
||||
* Description : A oldest incoming connection has been killed.
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* time.h
|
||||
*
|
||||
* Created on: May 31, 2016
|
||||
* Author: liuhan
|
||||
*/
|
||||
|
||||
#ifndef TIME_H_
|
||||
#define TIME_H_
|
||||
#include "osapi.h"
|
||||
#include "os_type.h"
|
||||
#include "lwip/sntp.h"
|
||||
|
||||
struct timeval {
|
||||
unsigned long tv_sec; /* seconds */
|
||||
unsigned long tv_usec; /* and microseconds */
|
||||
};
|
||||
|
||||
/***************************RTC TIME OPTION***************************************/
|
||||
// daylight settings
|
||||
// Base calculated with value obtained from NTP server (64 bits)
|
||||
#define sntp_base (*((uint64_t*)RTC_STORE0))
|
||||
// Timer value when base was obtained
|
||||
#define TIM_REF_SET(value) WRITE_PERI_REG(RTC_STORE2, value)
|
||||
#define TIM_REF_GET() READ_PERI_REG(RTC_STORE2)
|
||||
|
||||
// Setters and getters for CAL, TZ and DST.
|
||||
#define RTC_CAL_SET(val) do {uint32 value = READ_PERI_REG(RTC_STORE3);\
|
||||
value |= ((val) & 0x0000FFFF);\
|
||||
WRITE_PERI_REG(RTC_STORE3, value);\
|
||||
}while(0)
|
||||
#define RTC_DST_SET(val) do {uint32 value = READ_PERI_REG(RTC_STORE3);\
|
||||
value |= (((val)<<16) & 0x00010000);\
|
||||
WRITE_PERI_REG(RTC_STORE3, value);\
|
||||
}while(0)
|
||||
#define RTC_TZ_SET(val) do {uint32 value = READ_PERI_REG(RTC_STORE3);\
|
||||
value |= (((val)<<24) & 0xFF000000);\
|
||||
WRITE_PERI_REG(RTC_STORE3, value);\
|
||||
}while(0)
|
||||
|
||||
#define RTC_CAL_GET() (READ_PERI_REG(RTC_STORE3) & 0x0000FFFF)
|
||||
#define RTC_DST_GET() ((READ_PERI_REG(RTC_STORE3) & 0x00010000)>>16)
|
||||
#define RTC_TZ_GET() ((((int)READ_PERI_REG(RTC_STORE3)) & ((int)0xFF000000))>>24)
|
||||
void system_update_rtc(time_t t, uint32_t us);
|
||||
time_t sntp_get_rtc_time(sint32_t *us);
|
||||
|
||||
int gettimeofday(struct timeval* t, void* timezone);
|
||||
void updateTime(uint32 ms);
|
||||
bool configTime(int timezone, int daylightOffset, char *server1, char *server2, char *server3, bool enable);
|
||||
time_t time(time_t *t);
|
||||
unsigned long millis(void);
|
||||
unsigned long micros(void);
|
||||
#endif /* TIME_H_ */
|
|
@ -78,7 +78,7 @@ do{\
|
|||
#define mem_malloc(s) ({const char *file = mem_debug_file; pvPortMalloc(s, file, __LINE__);})
|
||||
#endif
|
||||
#ifndef mem_calloc
|
||||
#define mem_calloc(s) ({const char *file = mem_debug_file; pvPortCalloc(s, file, __LINE__);})
|
||||
#define mem_calloc(l, s) ({const char *file = mem_debug_file; pvPortCalloc(l, s, file, __LINE__);})
|
||||
#endif
|
||||
#ifndef mem_realloc
|
||||
#define mem_realloc(p, s) ({const char *file = mem_debug_file; pvPortRealloc(p, s, file, __LINE__);})
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
#ifndef LWIP_SNTP_H
|
||||
#define LWIP_SNTP_H
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef long time_t;
|
||||
|
||||
/** The maximum number of SNTP servers that can be set */
|
||||
#ifndef SNTP_MAX_SERVERS
|
||||
#define SNTP_MAX_SERVERS 3
|
||||
#endif
|
||||
|
||||
/** Set this to 1 to implement the callback function called by dhcp when
|
||||
* NTP servers are received. */
|
||||
#ifndef SNTP_GET_SERVERS_FROM_DHCP
|
||||
#define SNTP_GET_SERVERS_FROM_DHCP 0//LWIP_DHCP_GET_NTP_SRV
|
||||
#endif
|
||||
|
||||
/* Set this to 1 to support DNS names (or IP address strings) to set sntp servers */
|
||||
#ifndef SNTP_SERVER_DNS
|
||||
#define SNTP_SERVER_DNS 1
|
||||
#endif
|
||||
|
||||
bool sntp_get_timetype(void);
|
||||
void sntp_set_receive_time_size(void);
|
||||
/** One server address/name can be defined as default if SNTP_SERVER_DNS == 1:
|
||||
* #define SNTP_SERVER_ADDRESS "pool.ntp.org"
|
||||
*/
|
||||
uint32 sntp_get_current_timestamp();
|
||||
char* sntp_get_real_time(long t);
|
||||
|
||||
void sntp_init(void);
|
||||
void sntp_stop(void);
|
||||
|
||||
sint8 sntp_get_timezone(void);
|
||||
bool sntp_set_timezone(sint8 timezone);
|
||||
void sntp_setserver(u8_t idx, ip_addr_t *addr);
|
||||
ip_addr_t sntp_getserver(u8_t idx);
|
||||
|
||||
#if SNTP_SERVER_DNS
|
||||
void sntp_setservername(u8_t idx, char *server);
|
||||
char *sntp_getservername(u8_t idx);
|
||||
#endif /* SNTP_SERVER_DNS */
|
||||
|
||||
#if SNTP_GET_SERVERS_FROM_DHCP
|
||||
void sntp_servermode_dhcp(int set_servers_from_dhcp);
|
||||
#else /* SNTP_GET_SERVERS_FROM_DHCP */
|
||||
#define sntp_servermode_dhcp(x)
|
||||
#endif /* SNTP_GET_SERVERS_FROM_DHCP */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LWIP_SNTP_H */
|
|
@ -1,40 +0,0 @@
|
|||
#ifndef __DYNARR_H__
|
||||
#define __DYNARR_H__
|
||||
#include "user_interface.h"
|
||||
#include "c_stdio.h"
|
||||
#include "c_stdlib.h"
|
||||
|
||||
//#define DYNARR_DEBUG
|
||||
//#define DYNARR_ERROR
|
||||
|
||||
|
||||
typedef struct _dynarr{
|
||||
void* data_ptr;
|
||||
size_t used;
|
||||
size_t array_size;
|
||||
size_t data_size;
|
||||
} dynarr_t;
|
||||
|
||||
bool dynarr_init(dynarr_t* array_ptr, size_t array_size, size_t data_size);
|
||||
bool dynarr_resize(dynarr_t* array_ptr, size_t elements_to_add);
|
||||
bool dynarr_remove(dynarr_t* array_ptr, void* element_ptr);
|
||||
bool dynarr_add(dynarr_t* array_ptr, void* data_ptr, size_t data_size);
|
||||
bool dynarr_boundaryCheck(dynarr_t* array_ptr, void* element_ptr);
|
||||
bool dynarr_free(dynarr_t* array_ptr);
|
||||
|
||||
|
||||
#if 0 || defined(DYNARR_DEBUG) || defined(NODE_DEBUG)
|
||||
#define DYNARR_DBG(fmt, ...) c_printf("\n DYNARR_DBG(%s): "fmt"\n", __FUNCTION__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define DYNARR_DBG(...)
|
||||
|
||||
#endif
|
||||
|
||||
#if 0 || defined(DYNARR_ERROR) || defined(NODE_ERROR)
|
||||
#define DYNARR_ERR(fmt, ...) c_printf("\n DYNARR: "fmt"\n", ##__VA_ARGS__)
|
||||
#else
|
||||
#define DYNARR_ERR(...)
|
||||
|
||||
#endif
|
||||
|
||||
#endif // __DYNARR_H__
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* swtimer.h
|
||||
*
|
||||
* Created on: Aug 4, 2017
|
||||
* Author: anonymous
|
||||
*/
|
||||
|
||||
#ifndef APP_INCLUDE_PM_SWTIMER_H_
|
||||
#define APP_INCLUDE_PM_SWTIMER_H_
|
||||
|
||||
void swtmr_cb_register(void* timer_cb_ptr, uint8 suspend_policy);
|
||||
|
||||
#define SWTIMER_RESUME 0 //save remaining time
|
||||
#define SWTIMER_RESTART 1 //use timer_period as remaining time
|
||||
#define SWTIMER_IMMEDIATE 2 //fire timer immediately after resume
|
||||
#define SWTIMER_DROP 3 //disarm timer, do not resume
|
||||
|
||||
#if defined(TIMER_SUSPEND_ENABLE)
|
||||
#define SWTIMER_REG_CB(cb_ptr, suspend_policy) do{ \
|
||||
static bool cb_ptr##_registered_flag;\
|
||||
if(!cb_ptr##_registered_flag){ \
|
||||
cb_ptr##_registered_flag = true; \
|
||||
swtmr_cb_register(cb_ptr, suspend_policy);\
|
||||
} \
|
||||
}while(0);
|
||||
#else
|
||||
#define SWTIMER_REG_CB(...)
|
||||
#endif
|
||||
|
||||
#endif /* APP_INCLUDE_PM_SWTIMER_H_ */
|
|
@ -689,11 +689,15 @@ static inline void rtc_time_switch_to_system_clock(void)
|
|||
|
||||
static inline void rtc_time_tmrfn(void* arg);
|
||||
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
static void rtc_time_install_timer(void)
|
||||
{
|
||||
static ETSTimer tmr;
|
||||
|
||||
os_timer_setfn(&tmr,rtc_time_tmrfn,NULL);
|
||||
SWTIMER_REG_CB(rtc_time_tmrfn, SWTIMER_RESUME);
|
||||
//I believe the function rtc_time_tmrfn compensates for drift in the clock and updates rtc time accordingly, This timer should probably be resumed
|
||||
os_timer_arm(&tmr,10000,1);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
#ifndef __SW_TIMER_H__
|
||||
#define __SW_TIMER_H__
|
||||
#include "user_interface.h"
|
||||
//#define SWTMR_DEBUG
|
||||
#define USE_SWTMR_ERROR_STRINGS
|
||||
|
||||
#if defined(DEVELOP_VERSION)
|
||||
#define SWTMR_DEBUG
|
||||
#endif
|
||||
|
||||
#if defined(SWTMR_DEBUG)
|
||||
#define SWTMR_DBG(fmt, ...) dbg_printf("\tSWTIMER(%s):"fmt"\n", __FUNCTION__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define SWTMR_DBG(...)
|
||||
#endif
|
||||
|
||||
#if defined(NODE_ERROR)
|
||||
#define SWTMR_ERR(fmt, ...) NODE_ERR("%s"fmt"\n", "SWTIMER:", ##__VA_ARGS__)
|
||||
#else
|
||||
#define SWTMR_ERR(...)
|
||||
#endif
|
||||
|
||||
enum SWTMR_STATUS{
|
||||
SWTMR_FAIL = 0,
|
||||
SWTMR_OK = 1,
|
||||
|
||||
SWTMR_MALLOC_FAIL = 10,
|
||||
SWTMR_TIMER_NOT_ARMED,
|
||||
// SWTMR_NULL_PTR,
|
||||
|
||||
SWTMR_REGISTRY_NO_REGISTERED_TIMERS,
|
||||
|
||||
// SWTMR_SUSPEND_ARRAY_INITIALIZATION_FAILED,
|
||||
// SWTMR_SUSPEND_ARRAY_ADD_FAILED,
|
||||
// SWTMR_SUSPEND_ARRAY_REMOVE_FAILED,
|
||||
SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED,
|
||||
SWTMR_SUSPEND_TIMER_ALREADY_REARMED,
|
||||
SWTMR_SUSPEND_NO_SUSPENDED_TIMERS,
|
||||
SWTMR_SUSPEND_TIMER_NOT_SUSPENDED,
|
||||
|
||||
};
|
||||
|
||||
/* Global Function Declarations */
|
||||
void swtmr_register(void* timer_ptr);
|
||||
void swtmr_unregister(void* timer_ptr);
|
||||
int swtmr_suspend(os_timer_t* timer_ptr);
|
||||
int swtmr_resume(os_timer_t* timer_ptr);
|
||||
void swtmr_print_registry(void);
|
||||
void swtmr_print_suspended(void);
|
||||
void swtmr_print_timer_list(void);
|
||||
const char* swtmr_errorcode2str(int error_value);
|
||||
bool swtmr_suspended_test(os_timer_t* timer_ptr);
|
||||
#endif // __SW_TIMER_H__
|
|
@ -218,7 +218,9 @@ extern void luaL_dbgbreak(void);
|
|||
// #define GPIO_SAFE_NO_INTR_ENABLE
|
||||
#define ICACHE_STORE_TYPEDEF_ATTR __attribute__((aligned(4),packed))
|
||||
#define ICACHE_STORE_ATTR __attribute__((aligned(4)))
|
||||
#define ICACHE_RAM_ATTR __attribute__((section(".iram0.text")))
|
||||
#define ICACHE_RAM_STRING(x) ICACHE_RAM_STRING2(x)
|
||||
#define ICACHE_RAM_STRING2(x) #x
|
||||
#define ICACHE_RAM_ATTR __attribute__((section(".iram0.text." __FILE__ "." ICACHE_RAM_STRING(__LINE__))))
|
||||
#ifdef GPIO_SAFE_NO_INTR_ENABLE
|
||||
#define NO_INTR_CODE ICACHE_RAM_ATTR __attribute__ ((noinline))
|
||||
#else
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
#undef MBEDTLS_HAVE_ASM
|
||||
#undef MBEDTLS_HAVE_SSE2
|
||||
|
||||
#define MBEDTLS_HAVE_TIME
|
||||
#define MBEDTLS_HAVE_TIME_DATE
|
||||
// These are disabled until we have a real, working RTC-based gettimeofday
|
||||
#undef MBEDTLS_HAVE_TIME
|
||||
#undef MBEDTLS_HAVE_TIME_DATE
|
||||
|
||||
#define MBEDTLS_PLATFORM_MEMORY
|
||||
#undef MBEDTLS_PLATFORM_NO_STD_FUNCTIONS
|
||||
|
|
|
@ -76,5 +76,9 @@
|
|||
//#define LUA_USE_MODULES_WS2812_EFFECTS
|
||||
//#define LUA_USE_MODULES_XPT2046
|
||||
|
||||
//debug modules
|
||||
//#define LUA_USE_MODULES_SWTMR_DBG //SWTMR timer suspend Debug functions
|
||||
|
||||
|
||||
#endif /* LUA_CROSS_COMPILER */
|
||||
#endif /* __USER_MODULES_H__ */
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
#ifndef __USER_VERSION_H__
|
||||
#define __USER_VERSION_H__
|
||||
|
||||
#define NODE_VERSION_MAJOR 2U
|
||||
#define NODE_VERSION_MINOR 1U
|
||||
#define NODE_VERSION_REVISION 0U
|
||||
#define NODE_VERSION_INTERNAL 0U
|
||||
#include "version.h" /* ESP firmware header */
|
||||
|
||||
#define NODE_VERSION_MAJOR ESP_SDK_VERSION_MAJOR
|
||||
#define NODE_VERSION_MINOR ESP_SDK_VERSION_MINOR
|
||||
#define NODE_VERSION_REVISION ESP_SDK_VERSION_PATCH
|
||||
#define NODE_VERSION_INTERNAL 0
|
||||
|
||||
#define NODE_VERSION_STR(x) #x
|
||||
#define NODE_VERSION_XSTR(x) NODE_VERSION_STR(x)
|
||||
|
||||
#define NODE_VERSION "NodeMCU " ESP_SDK_VERSION_STRING "." NODE_VERSION_XSTR(NODE_VERSION_INTERNAL)
|
||||
|
||||
#define NODE_VERSION "NodeMCU 2.1.0"
|
||||
#ifndef BUILD_DATE
|
||||
#define BUILD_DATE "unspecified"
|
||||
#define BUILD_DATE "unspecified"
|
||||
#endif
|
||||
|
||||
extern char SDK_VERSION[];
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include C_HEADER_STRING
|
||||
#ifndef LUA_CROSS_COMPILER
|
||||
#include "vfs.h"
|
||||
#include "user_interface.h"
|
||||
#else
|
||||
#endif
|
||||
|
||||
|
@ -939,6 +940,11 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
|
|||
}
|
||||
if (L != NULL && (mode & EGC_ALWAYS)) /* always collect memory if requested */
|
||||
luaC_fullgc(L);
|
||||
#ifndef LUA_CROSS_COMPILER
|
||||
if (L != NULL && (mode & EGC_ON_MEM_LIMIT) && G(L)->memlimit < 0 &&
|
||||
(system_get_free_heap_size() < (-G(L)->memlimit)))
|
||||
luaC_fullgc(L);
|
||||
#endif
|
||||
if(nsize > osize && L != NULL) {
|
||||
#if defined(LUA_STRESS_EMERGENCY_GC)
|
||||
luaC_fullgc(L);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "lstate.h"
|
||||
#include "c_types.h"
|
||||
|
||||
void legc_set_mode(lua_State *L, int mode, unsigned limit) {
|
||||
void legc_set_mode(lua_State *L, int mode, int limit) {
|
||||
global_State *g = G(L);
|
||||
|
||||
g->egcmode = mode;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#define EGC_ON_MEM_LIMIT 2 // run EGC when an upper memory limit is hit
|
||||
#define EGC_ALWAYS 4 // always run EGC before an allocation
|
||||
|
||||
void legc_set_mode(lua_State *L, int mode, unsigned limit);
|
||||
void legc_set_mode(lua_State *L, int mode, int limit);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ typedef struct global_State {
|
|||
Mbuffer buff; /* temporary buffer for string concatentation */
|
||||
lu_mem GCthreshold;
|
||||
lu_mem totalbytes; /* number of bytes currently allocated */
|
||||
lu_mem memlimit; /* maximum number of bytes that can be allocated, 0 = no limit. */
|
||||
l_mem memlimit; /* maximum number of bytes that can be allocated, 0 = no limit. <0 used with EGC_ON_MEM_LIMIT when free heap falls below -memlimit */
|
||||
lu_mem estimate; /* an estimate of number of bytes actually in use */
|
||||
lu_mem gcdept; /* how much GC is `behind schedule' */
|
||||
int gcpause; /* size of pause between successive GCs */
|
||||
|
|
|
@ -441,6 +441,7 @@ sint16 ICACHE_FLASH_ATTR espconn_recv(struct espconn *espconn, void *mem, size_t
|
|||
espconn_msg *pnode = NULL;
|
||||
bool value = false;
|
||||
int bytes_used = 0;
|
||||
struct tcp_pcb *tpcb = NULL;
|
||||
if (espconn == NULL || mem == NULL || len == 0)
|
||||
return ESPCONN_ARG;
|
||||
|
||||
|
@ -454,13 +455,15 @@ sint16 ICACHE_FLASH_ATTR espconn_recv(struct espconn *espconn, void *mem, size_t
|
|||
len = bytes_used;
|
||||
}
|
||||
ringbuf_memcpy_from(mem, pnode->readbuf, len);
|
||||
espconn_recv_unhold(pnode->pespconn);
|
||||
tpcb = pnode->pcommon.pcb;
|
||||
if (tpcb && tpcb->state == ESTABLISHED)
|
||||
tcp_recved(pnode->pcommon.pcb, len);
|
||||
return len;
|
||||
} else {
|
||||
return ESPCONN_OK;
|
||||
}
|
||||
} else{
|
||||
return ESPCONN_OK;
|
||||
return ESPCONN_MEM;
|
||||
}
|
||||
} else{
|
||||
return ESPCONN_ARG;
|
||||
|
|
|
@ -231,6 +231,10 @@ void ICACHE_FLASH_ATTR espconn_tcp_memp_free(espconn_msg *pmemp)
|
|||
if (pmemp == NULL)
|
||||
return;
|
||||
|
||||
/*Enable block option for fetches the data proactive*/
|
||||
if (espconn_manual_recv_disabled(pmemp))
|
||||
espconn_list_delete(&plink_active, pmemp);
|
||||
|
||||
if (pmemp->espconn_mode == ESPCONN_TCPSERVER_MODE){
|
||||
if (pmemp->pespconn != NULL && pmemp->pespconn->proto.tcp != NULL)
|
||||
os_free(pmemp->pespconn->proto.tcp);
|
||||
|
@ -431,11 +435,13 @@ espconn_Task(os_event_t *events)
|
|||
case SIG_ESPCONN_ERRER:
|
||||
/*remove the node from the client's active connection list*/
|
||||
espconn_list_delete(&plink_active, task_msg);
|
||||
espconn_tcp_reconnect(task_msg);
|
||||
if (espconn_manual_recv_enabled(task_msg))
|
||||
espconn_list_delete(&plink_active, task_msg);
|
||||
break;
|
||||
case SIG_ESPCONN_CLOSE:
|
||||
/*remove the node from the client's active connection list*/
|
||||
espconn_list_delete(&plink_active, task_msg);
|
||||
if (espconn_manual_recv_enabled(task_msg))
|
||||
espconn_list_delete(&plink_active, task_msg);
|
||||
espconn_tcp_disconnect_successful(task_msg);
|
||||
break;
|
||||
default:
|
||||
|
@ -538,6 +544,66 @@ void ICACHE_FLASH_ATTR espconn_tcp_disconnect(espconn_msg *pdiscon,u8 type)
|
|||
}
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_tcp_recv
|
||||
* Description : Data has been received on this pcb.
|
||||
* Parameters : arg -- Additional argument to pass to the callback function
|
||||
* pcb -- The connection pcb which received data
|
||||
* p -- The received data (or NULL when the connection has been closed!)
|
||||
* err -- An error code if there has been an error receiving
|
||||
* Returns : ERR_ABRT: if you have called tcp_abort from within the function!
|
||||
*******************************************************************************/
|
||||
static err_t ICACHE_FLASH_ATTR
|
||||
espconn_tcp_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
|
||||
{
|
||||
espconn_msg *precv_cb = arg;
|
||||
struct pbuf *pthis = NULL;
|
||||
uint8_t *ring = NULL;
|
||||
size_t bytes_used = 0;
|
||||
|
||||
tcp_arg(pcb, arg);
|
||||
|
||||
if (precv_cb->readbuf == NULL) {
|
||||
precv_cb->readbuf = ringbuf_new(TCP_WND);
|
||||
if (precv_cb->readbuf == NULL)
|
||||
return ESPCONN_MEM;
|
||||
}
|
||||
|
||||
if (err == ERR_OK) {
|
||||
precv_cb->pcommon.recv_check = 0;
|
||||
if (p != NULL) {
|
||||
/*store the data to the adapter for application fetches it proactive*/
|
||||
for (pthis = p; pthis != NULL ; pthis = pthis->next) {
|
||||
ring = ringbuf_memcpy_into(precv_cb->readbuf, pthis->payload, pthis->len);
|
||||
if (ring)
|
||||
pbuf_free(pthis);
|
||||
else
|
||||
break;
|
||||
}
|
||||
bytes_used = ringbuf_bytes_used(precv_cb->readbuf);
|
||||
|
||||
/*switch the state of espconn for application process*/
|
||||
precv_cb->pespconn->state = ESPCONN_READ;
|
||||
precv_cb->pcommon.pcb = pcb;
|
||||
if (precv_cb->pespconn->recv_callback != NULL) {
|
||||
precv_cb->pespconn->recv_callback(precv_cb->pespconn, NULL, bytes_used);
|
||||
}
|
||||
|
||||
/*switch the state of espconn for next packet copy*/
|
||||
if (pcb->state == ESTABLISHED)
|
||||
precv_cb->pespconn->state = ESPCONN_CONNECT;
|
||||
} else {
|
||||
if (precv_cb->preverse) {
|
||||
espconn_server_close(precv_cb, pcb, 0);
|
||||
} else {
|
||||
espconn_client_close(precv_cb, pcb, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
///////////////////////////////client function/////////////////////////////////
|
||||
/******************************************************************************
|
||||
* FunctionName : espconn_client_close
|
||||
|
@ -933,6 +999,10 @@ espconn_client_connect(void *arg, struct tcp_pcb *tpcb, err_t err)
|
|||
pcon->pespconn->proto.tcp->connect_callback(pcon->pespconn);
|
||||
}
|
||||
|
||||
/*Enable block option for fetches the data proactive*/
|
||||
if (espconn_manual_recv_disabled(pcon))
|
||||
tcp_recv(tpcb, espconn_tcp_recv);
|
||||
|
||||
/*Enable keep alive option*/
|
||||
if (espconn_keepalive_disabled(pcon))
|
||||
espconn_keepalive_enable(tpcb);
|
||||
|
@ -1358,6 +1428,10 @@ espconn_tcp_accept(void *arg, struct tcp_pcb *pcb, err_t err)
|
|||
paccept->pespconn->proto.tcp->connect_callback(paccept->pespconn);
|
||||
}
|
||||
|
||||
/*Enable block option for fetches the data proactive*/
|
||||
if (espconn_manual_recv_disabled(paccept))
|
||||
tcp_recv(pcb, espconn_tcp_recv);
|
||||
|
||||
/*Enable keep alive option*/
|
||||
if (espconn_keepalive_disabled(paccept))
|
||||
espconn_keepalive_enable(pcb);
|
||||
|
|
|
@ -237,9 +237,10 @@ espconn_udp_sendto(void *arg, uint8 *psent, uint16 length)
|
|||
|
||||
if(wifi_get_opmode() == ESPCONN_AP_STA && default_interface == ESPCONN_AP_STA && sta_netif != NULL && ap_netif != NULL)
|
||||
{
|
||||
if(netif_is_up(sta_netif) && netif_is_up(ap_netif) && \
|
||||
ip_addr_isbroadcast(&upcb->remote_ip, sta_netif) && \
|
||||
ip_addr_isbroadcast(&upcb->remote_ip, ap_netif)) {
|
||||
if( netif_is_up(sta_netif) && \
|
||||
netif_is_up(ap_netif) && \
|
||||
ip_addr_isbroadcast(&dst_ip, sta_netif) && \
|
||||
ip_addr_isbroadcast(&dst_ip, ap_netif)) {
|
||||
|
||||
p_temp = pbuf_alloc(PBUF_TRANSPORT, datalen, PBUF_RAM);
|
||||
if (pbuf_copy (p_temp,p) != ERR_OK) {
|
||||
|
|
|
@ -1039,6 +1039,7 @@ mdns_reg(struct mdns_info *info) {
|
|||
os_timer_disarm(&mdns_timer);
|
||||
}
|
||||
}
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
/**
|
||||
* Initialize the resolver: set up the UDP pcb and configure the default server
|
||||
|
@ -1129,6 +1130,8 @@ mdns_init(struct mdns_info *info) {
|
|||
|
||||
os_timer_disarm(&mdns_timer);
|
||||
os_timer_setfn(&mdns_timer, (os_timer_func_t *)mdns_reg,ms_info);
|
||||
SWTIMER_REG_CB(mdns_reg, SWTIMER_RESTART);
|
||||
//going on the above comment, it's probably a good idea to let mdns_reg run it's course. not sure if the 1 second timing is important, so lets restart it to be safe.
|
||||
os_timer_arm(&mdns_timer, 1000, 1);
|
||||
}
|
||||
}
|
||||
|
|
1185
app/lwip/core/sntp.c
1185
app/lwip/core/sntp.c
File diff suppressed because it is too large
Load Diff
|
@ -500,7 +500,7 @@ tcp_listen_input(struct tcp_pcb_listen *pcb)
|
|||
/* For incoming segments with the ACK flag set, respond with a
|
||||
RST. */
|
||||
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
|
||||
tcp_rst(ackno + 1, seqno + tcplen,
|
||||
tcp_rst(ackno, seqno + tcplen,
|
||||
ip_current_dest_addr(), ip_current_src_addr(),
|
||||
tcphdr->dest, tcphdr->src);
|
||||
} else if (flags & TCP_SYN) {//<2F>յ<EFBFBD>SYN<59><4E><EFBFBD><EFBFBD>
|
||||
|
|
|
@ -461,6 +461,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
|||
return ERR_MEM;
|
||||
}
|
||||
|
||||
#if !LWIP_NETIF_TX_SINGLE_PBUF
|
||||
/*
|
||||
* Phase 2: Chain a new pbuf to the end of pcb->unsent.
|
||||
*
|
||||
|
@ -510,6 +511,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
|
|||
pos += seglen;
|
||||
queuelen += pbuf_clen(concat_p);
|
||||
}
|
||||
#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
|
||||
} else {
|
||||
#if TCP_OVERSIZE
|
||||
LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
|
||||
|
@ -1336,6 +1338,12 @@ tcp_rexmit(struct tcp_pcb *pcb)
|
|||
}
|
||||
seg->next = *cur_seg;
|
||||
*cur_seg = seg;
|
||||
#if TCP_OVERSIZE
|
||||
if (seg->next == NULL) {
|
||||
/* the retransmitted segment is last in unsent, so reset unsent_oversize */
|
||||
pcb->unsent_oversize = 0;
|
||||
}
|
||||
#endif /* TCP_OVERSIZE */
|
||||
|
||||
++pcb->nrtx;
|
||||
|
||||
|
|
|
@ -353,6 +353,8 @@ static void mbedtls_msg_free(pmbedtls_msg *msg)
|
|||
os_free((*msg)->ssl.out_buf);
|
||||
(*msg)->ssl.out_buf = NULL;
|
||||
}
|
||||
if((*msg)->pfinished != NULL)
|
||||
mbedtls_finished_free(&(*msg)->pfinished);
|
||||
#endif
|
||||
mbedtls_entropy_free(&(*msg)->entropy);
|
||||
mbedtls_ssl_free(&(*msg)->ssl);
|
||||
|
@ -427,10 +429,6 @@ static void mbedtls_fail_info(espconn_msg *pinfo, int ret)
|
|||
TLSmsg = pinfo->pssl;
|
||||
lwIP_REQUIRE_ACTION(TLSmsg,exit,);
|
||||
|
||||
if (TLSmsg->quiet) {
|
||||
mbedtls_ssl_close_notify(&TLSmsg->ssl);
|
||||
}
|
||||
|
||||
/* Don't complain to console if we've been told the other end is hanging
|
||||
* up. That's entirely normal and not worthy of the confusion it sows!
|
||||
*/
|
||||
|
@ -441,6 +439,7 @@ static void mbedtls_fail_info(espconn_msg *pinfo, int ret)
|
|||
} else {
|
||||
os_printf("client's data invalid protocol\n");
|
||||
}
|
||||
mbedtls_ssl_close_notify(&TLSmsg->ssl);
|
||||
} else{
|
||||
if (pinfo->preverse != NULL) {
|
||||
os_printf("server handshake failed!\n");
|
||||
|
@ -564,6 +563,11 @@ static void espconn_close_internal(void *arg, netconn_event event_type)
|
|||
ssl_reerr = pssl_recon->pcommon.err;
|
||||
hs_status = pssl_recon->hs_status;
|
||||
if (espconn != NULL) {
|
||||
//clear pcommon parameters.
|
||||
pssl_recon->pcommon.write_flag = false;
|
||||
pssl_recon->pcommon.ptrbuf = NULL;
|
||||
pssl_recon->pcommon.cntr = 0;
|
||||
pssl_recon->pcommon.err = 0;
|
||||
espconn = pssl_recon->preverse;
|
||||
} else {
|
||||
espconn = pssl_recon->pespconn;
|
||||
|
@ -667,6 +671,11 @@ again:
|
|||
offerset += sizeof(file_head) + pfile_param->file_head.file_length;
|
||||
goto again;
|
||||
}
|
||||
/*Optional is load the cert*/
|
||||
if (auth_info->auth_type == ESPCONN_CERT_OWN && os_memcmp(pfile_param->file_head.file_name, "certificate", os_strlen("certificate")) != 0){
|
||||
offerset += sizeof(file_head) + pfile_param->file_head.file_length;
|
||||
goto again;
|
||||
}
|
||||
load_buf = (uint8_t *) os_zalloc( pfile_param->file_head.file_length + FILE_OFFSET);
|
||||
if (load_buf == NULL){
|
||||
os_free(pfile_param);
|
||||
|
@ -839,6 +848,9 @@ int __attribute__((weak)) mbedtls_parse_internal(int socket, sint8 error)
|
|||
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == 0){
|
||||
ret = ESPCONN_OK;
|
||||
break;
|
||||
} else if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY){
|
||||
ret = ESPCONN_OK;
|
||||
mbedtls_ssl_close_notify(&TLSmsg->ssl);
|
||||
} else{
|
||||
break;
|
||||
}
|
||||
|
@ -891,6 +903,9 @@ int __attribute__((weak)) mbedtls_parse_internal(int socket, sint8 error)
|
|||
}
|
||||
|
||||
system_soft_wdt_stop();
|
||||
uint8 cpu_freq;
|
||||
cpu_freq = system_get_cpu_freq();
|
||||
system_update_cpu_freq(160);
|
||||
while ((ret = mbedtls_ssl_handshake(&TLSmsg->ssl)) != 0) {
|
||||
|
||||
if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
|
@ -901,6 +916,7 @@ int __attribute__((weak)) mbedtls_parse_internal(int socket, sint8 error)
|
|||
}
|
||||
}
|
||||
system_soft_wdt_restart();
|
||||
system_update_cpu_freq(cpu_freq);
|
||||
lwIP_REQUIRE_NOERROR(ret, exit);
|
||||
/**/
|
||||
TLSmsg->quiet = mbedtls_handshake_result(TLSmsg);
|
||||
|
@ -937,6 +953,9 @@ int __attribute__((weak)) mbedtls_parse_internal(int socket, sint8 error)
|
|||
exit:
|
||||
if (ret != ESPCONN_OK){
|
||||
mbedtls_fail_info(Threadmsg, ret);
|
||||
if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY){
|
||||
Threadmsg->hs_status = ESPCONN_OK;
|
||||
}
|
||||
ets_post(lwIPThreadPrio, NETCONN_EVENT_CLOSE,(uint32)Threadmsg);
|
||||
}
|
||||
return ret;
|
||||
|
|
|
@ -302,6 +302,20 @@ static err_t do_accepted(void *arg, struct tcp_pcb *newpcb, err_t err)
|
|||
lwIP_netconn *newconn = NULL;
|
||||
lwIP_netconn *conn = arg;
|
||||
err = ERR_OK;
|
||||
|
||||
//Avoid two TCP connections coming in simultaneously
|
||||
struct tcp_pcb *pactive_pcb;
|
||||
int active_pcb_num=0;
|
||||
for(pactive_pcb = tcp_active_pcbs; pactive_pcb != NULL; pactive_pcb = pactive_pcb->next){
|
||||
if (pactive_pcb->state == ESTABLISHED ||pactive_pcb->state == SYN_RCVD){
|
||||
active_pcb_num++;
|
||||
if (active_pcb_num > MEMP_NUM_TCP_PCB){
|
||||
ESP_LOG("%s %d active_pcb_number:%d\n",__FILE__, __LINE__,active_pcb_num);
|
||||
return ERR_MEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lwIP_REQUIRE_ACTION(conn, exit, err = ESP_ARG);
|
||||
/* We have to set the callback here even though
|
||||
* the new socket is unknown. conn->socket is marked as -1. */
|
||||
|
@ -738,23 +752,30 @@ int lwip_close(int s)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (sock->conn->state != NETCONN_STATE_ERROR){
|
||||
tcp_recv(sock->conn->tcp, NULL);
|
||||
err = tcp_close(sock->conn->tcp);
|
||||
/*Do not set callback function when tcp->state is LISTEN.
|
||||
Avoid memory overlap when conn->tcp changes from
|
||||
struct tcp_bcb to struct tcp_pcb_listen after lwip_listen.*/
|
||||
if (sock->conn->tcp->state != LISTEN)
|
||||
{
|
||||
if (sock->conn->state != NETCONN_STATE_ERROR){
|
||||
tcp_recv(sock->conn->tcp, NULL);
|
||||
err = tcp_close(sock->conn->tcp);
|
||||
|
||||
if (err != ERR_OK)
|
||||
{
|
||||
/* closing failed, try again later */
|
||||
tcp_recv(sock->conn->tcp, recv_tcp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* closing succeeded */
|
||||
remove_tcp(sock->conn);
|
||||
free_netconn(sock->conn);
|
||||
free_socket(sock);
|
||||
return ERR_OK;
|
||||
if (err != ERR_OK)
|
||||
{
|
||||
/* closing failed, try again later */
|
||||
tcp_recv(sock->conn->tcp, recv_tcp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* closing succeeded */
|
||||
remove_tcp(sock->conn);
|
||||
} else {
|
||||
tcp_close(sock->conn->tcp);
|
||||
}
|
||||
free_netconn(sock->conn);
|
||||
free_socket(sock);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
int lwip_write(int s, const void *data, size_t size)
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
|
||||
#############################################################
|
||||
# Required variables for each makefile
|
||||
# Discard this section from all parent makefiles
|
||||
# Expected variables (with automatic defaults):
|
||||
# CSRCS (all "C" files in the dir)
|
||||
# SUBDIRS (all subdirs with a Makefile)
|
||||
# GEN_LIBS - list of libs to be generated ()
|
||||
# GEN_IMAGES - list of images to be generated ()
|
||||
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||
# subdir/lib to be extracted and rolled up into
|
||||
# a generated lib/image xxx.a ()
|
||||
#
|
||||
ifndef PDIR
|
||||
GEN_LIBS = libmisc.a
|
||||
endif
|
||||
|
||||
STD_CFLAGS=-std=gnu11 -Wimplicit
|
||||
|
||||
#############################################################
|
||||
# Configuration i.e. compile options etc.
|
||||
# Target specific stuff (defines etc.) goes in here!
|
||||
# Generally values applying to a tree are captured in the
|
||||
# makefile at its root level - these are then overridden
|
||||
# for a subtree within the makefile rooted therein
|
||||
#
|
||||
#DEFINES +=
|
||||
|
||||
#############################################################
|
||||
# Recursion Magic - Don't touch this!!
|
||||
#
|
||||
# Each subtree potentially has an include directory
|
||||
# corresponding to the common APIs applicable to modules
|
||||
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||
# of a module can only contain the include directories up
|
||||
# its parent path, and not its siblings
|
||||
#
|
||||
# Required for each makefile to inherit from the parent
|
||||
#
|
||||
|
||||
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||
INCLUDES += -I ./
|
||||
INCLUDES += -I ./include
|
||||
INCLUDES += -I ../include
|
||||
INCLUDES += -I ../../include
|
||||
INCLUDES += -I ../lua
|
||||
INCLUDES += -I ../platform
|
||||
INCLUDES += -I ../libc
|
||||
|
||||
PDIR := ../$(PDIR)
|
||||
sinclude $(PDIR)Makefile
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
#include "misc/dynarr.h"
|
||||
|
||||
#define ARRAY_PTR_CHECK if(array_ptr == NULL || array_ptr->data_ptr == NULL){\
|
||||
/**/DYNARR_DBG("array not initialized");\
|
||||
return false; \
|
||||
}
|
||||
|
||||
bool dynarr_init(dynarr_t* array_ptr, size_t array_size, size_t data_size){
|
||||
if(array_ptr == NULL || data_size == 0 || array_size == 0){
|
||||
/**/DYNARR_DBG("Invalid parameter: array_ptr(%p) data_size(%u) array_size(%u)", array_ptr, data_size, array_size);
|
||||
return false;
|
||||
}
|
||||
if(array_ptr->data_ptr != NULL ){
|
||||
/**/DYNARR_DBG("Array already initialized: array_ptr->data_ptr=%p", array_ptr->data_ptr);
|
||||
return false;
|
||||
}
|
||||
/**/DYNARR_DBG("Array parameters:\n\t\t\tarray_size(%u)\n\t\t\tdata_size(%u)\n\t\t\ttotal size(bytes):%u", array_size, data_size, (array_size * data_size));
|
||||
|
||||
void* temp_array = c_zalloc(array_size * data_size);
|
||||
if(temp_array == NULL){
|
||||
/**/DYNARR_ERR("malloc FAIL! req:%u free:%u", (array_size * data_size), system_get_free_heap_size());
|
||||
return false;
|
||||
}
|
||||
|
||||
array_ptr->data_ptr = temp_array;
|
||||
array_ptr->array_size = array_size;
|
||||
array_ptr->data_size = data_size;
|
||||
array_ptr->used = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dynarr_resize(dynarr_t* array_ptr, size_t elements_to_add){
|
||||
ARRAY_PTR_CHECK;
|
||||
|
||||
if(elements_to_add <= 0){
|
||||
/**/DYNARR_DBG("Invalid qty: elements_to_add=%u", elements_to_add);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t new_array_size = array_ptr->array_size + elements_to_add;
|
||||
|
||||
/**/DYNARR_DBG("old size=%u\tnew size=%u\tmem used=%u",
|
||||
array_ptr->array_size, new_array_size, (new_array_size * array_ptr->data_size));
|
||||
|
||||
void* temp_array_p = c_realloc(array_ptr->data_ptr, new_array_size * array_ptr->data_size);
|
||||
if(temp_array_p == NULL){
|
||||
/**/DYNARR_ERR("malloc FAIL! req:%u free:%u", (new_array_size * array_ptr->data_size), system_get_free_heap_size());
|
||||
return false;
|
||||
}
|
||||
|
||||
array_ptr->data_ptr = temp_array_p;
|
||||
|
||||
size_t prev_size = array_ptr->array_size;
|
||||
|
||||
array_ptr->array_size = new_array_size;
|
||||
|
||||
//set memory to 0 for newly added array elements
|
||||
memset((uint8*) array_ptr->data_ptr + (prev_size * array_ptr->data_size), 0, (elements_to_add * array_ptr->data_size));
|
||||
|
||||
/**/DYNARR_DBG("Array successfully resized");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dynarr_remove(dynarr_t* array_ptr, void* element_to_remove){
|
||||
ARRAY_PTR_CHECK;
|
||||
|
||||
uint8* element_ptr = element_to_remove;
|
||||
uint8* data_ptr = array_ptr->data_ptr;
|
||||
|
||||
if(dynarr_boundaryCheck(array_ptr, element_to_remove) == FALSE){
|
||||
return false;
|
||||
}
|
||||
|
||||
//overwrite element to be removed by shifting all elements to the left
|
||||
memmove(element_ptr, element_ptr + array_ptr->data_size, (array_ptr->array_size - 1) * array_ptr->data_size - (element_ptr - data_ptr));
|
||||
|
||||
//clear newly freed element
|
||||
memset(data_ptr + ((array_ptr->array_size-1) * array_ptr->data_size), 0, array_ptr->data_size);
|
||||
|
||||
//decrement array used since we removed an element
|
||||
array_ptr->used--;
|
||||
/**/DYNARR_DBG("element(%p) removed from array", element_ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dynarr_add(dynarr_t* array_ptr, void* data_ptr, size_t data_size){
|
||||
ARRAY_PTR_CHECK;
|
||||
|
||||
if(data_size != array_ptr->data_size){
|
||||
/**/DYNARR_DBG("Invalid data size: data_size(%u) != arr->data_size(%u)", data_size, array_ptr->data_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(array_ptr->array_size == array_ptr->used){
|
||||
if(!dynarr_resize(array_ptr, (array_ptr->array_size/2))){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
memcpy(((uint8*)array_ptr->data_ptr + (array_ptr->used * array_ptr->data_size)), data_ptr, array_ptr->data_size);
|
||||
|
||||
array_ptr->used++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dynarr_boundaryCheck(dynarr_t* array_ptr, void* element_to_check){
|
||||
ARRAY_PTR_CHECK;
|
||||
|
||||
uint8* data_ptr = array_ptr->data_ptr;
|
||||
uint8* element_ptr = element_to_check;
|
||||
|
||||
if(element_ptr < data_ptr || ((element_ptr - data_ptr) / array_ptr->data_size) > array_ptr->array_size - 1){
|
||||
/**/DYNARR_DBG("element_ptr(%p) out of bounds: first element ptr:%p last element ptr:%p",
|
||||
element_ptr, data_ptr, data_ptr + ((array_ptr->array_size - 1) * array_ptr->data_size));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dynarr_free(dynarr_t* array_ptr){
|
||||
ARRAY_PTR_CHECK;
|
||||
|
||||
c_free(array_ptr->data_ptr);
|
||||
|
||||
array_ptr->data_ptr=NULL;
|
||||
array_ptr->array_size = array_ptr->used = 0;
|
||||
|
||||
/**/DYNARR_DBG("array freed");
|
||||
return true;
|
||||
}
|
||||
|
|
@ -190,7 +190,7 @@ static void cron_handle_tmr() {
|
|||
struct rtc_timeval tv;
|
||||
rtctime_gettimeofday(&tv);
|
||||
if (tv.tv_sec == 0) { // Wait for RTC time
|
||||
ets_timer_arm_new(&cron_timer, 1000, 0, 1);
|
||||
os_timer_arm(&cron_timer, 1000, 0);
|
||||
return;
|
||||
}
|
||||
time_t t = tv.tv_sec;
|
||||
|
@ -202,7 +202,7 @@ static void cron_handle_tmr() {
|
|||
diff += 60000;
|
||||
gmtime_r(&t, &tm);
|
||||
}
|
||||
ets_timer_arm_new(&cron_timer, diff, 0, 1);
|
||||
os_timer_arm(&cron_timer, diff, 0);
|
||||
cron_handle_time(tm.tm_mon + 1, tm.tm_mday, tm.tm_wday, tm.tm_hour, tm.tm_min);
|
||||
}
|
||||
|
||||
|
@ -220,11 +220,15 @@ static const LUA_REG_TYPE cron_map[] = {
|
|||
{ LSTRKEY( "reset" ), LFUNCVAL( lcron_reset ) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
int luaopen_cron( lua_State *L ) {
|
||||
ets_timer_disarm(&cron_timer);
|
||||
ets_timer_setfn(&cron_timer, cron_handle_tmr, 0);
|
||||
ets_timer_arm_new(&cron_timer, 1000, 0, 1);
|
||||
os_timer_disarm(&cron_timer);
|
||||
os_timer_setfn(&cron_timer, cron_handle_tmr, 0);
|
||||
SWTIMER_REG_CB(cron_handle_tmr, SWTIMER_RESTART);
|
||||
//cron_handle_tmr determines when to execute a scheduled cron job
|
||||
//My guess: To be sure to give the other modules required by cron enough time to get to a ready state, restart cron_timer.
|
||||
os_timer_arm(&cron_timer, 1000, 0);
|
||||
luaL_rometatable(L, "cron.entry", (void *)cronent_map);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -134,6 +134,8 @@ static int ds18b20_lua_setting(lua_State *L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
// Reads sensor values from all devices
|
||||
// Lua: ds18b20.read(function(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) print(INDEX, ROM, RES, TEMP, TEMP_DEC, PAR) end, ROM[, FAMILY])
|
||||
static int ds18b20_lua_read(lua_State *L) {
|
||||
|
@ -173,6 +175,9 @@ static int ds18b20_lua_read(lua_State *L) {
|
|||
onewire_write(ds18b20_bus_pin, DS18B20_ROM_SKIP, 0);
|
||||
onewire_write(ds18b20_bus_pin, DS18B20_FUNC_CONVERT, 1);
|
||||
os_timer_setfn(&ds18b20_timer, (os_timer_func_t *)ds18b20_lua_readoutdone, NULL);
|
||||
SWTIMER_REG_CB(ds18b20_lua_readoutdone, SWTIMER_DROP);
|
||||
//The function ds18b20_lua_readoutdone reads the temperature from the sensor(s) after a set amount of time depending on temperature resolution
|
||||
//MY guess: If this timer manages to get suspended before it fires and the temperature data is time sensitive then resulting data would be invalid and should be discarded
|
||||
|
||||
switch (ds18b20_device_res) {
|
||||
case (9):
|
||||
|
|
|
@ -212,12 +212,15 @@ static void enduser_setup_connected_callback()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#include "pm/swtimer.h"
|
||||
static void enduser_setup_check_station_start(void)
|
||||
{
|
||||
ENDUSER_SETUP_DEBUG("enduser_setup_check_station_start");
|
||||
|
||||
os_timer_setfn(&(state->check_station_timer), enduser_setup_check_station, NULL);
|
||||
SWTIMER_REG_CB(enduser_setup_check_station, SWTIMER_RESUME);
|
||||
//The function enduser_setup_check_station checks for a successful connection to the configured AP
|
||||
//My guess: I'm not sure about whether or not user feedback is given via the web interface, but I don't see a problem with letting this timer resume.
|
||||
os_timer_arm(&(state->check_station_timer), 3*1000, TRUE);
|
||||
}
|
||||
|
||||
|
@ -317,6 +320,9 @@ static void enduser_setup_check_station(void *p)
|
|||
if (!manual)
|
||||
{
|
||||
os_timer_setfn(&(state->shutdown_timer), enduser_setup_stop_callback, NULL);
|
||||
SWTIMER_REG_CB(enduser_setup_stop_callback, SWTIMER_RESUME);
|
||||
//The function enduser_setup_stop_callback frees services and resources used by enduser setup.
|
||||
//My guess: Since it would lead to a memory leak, it's probably best to resume this timer.
|
||||
os_timer_arm(&(state->shutdown_timer), 10*1000, FALSE);
|
||||
}
|
||||
}
|
||||
|
@ -664,6 +670,8 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data
|
|||
|
||||
struct station_config *cnf = luaM_malloc(lua_getstate(), sizeof(struct station_config));
|
||||
c_memset(cnf, 0, sizeof(struct station_config));
|
||||
cnf->threshold.rssi = -127;
|
||||
cnf->threshold.authmode = AUTH_OPEN;
|
||||
|
||||
int err;
|
||||
err = enduser_setup_http_urldecode(cnf->ssid, name_str_start, name_str_len, sizeof(cnf->ssid));
|
||||
|
|
|
@ -981,6 +981,7 @@ static sint8 socket_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
|
|||
return espconn_status;
|
||||
}
|
||||
|
||||
#include "pm/swtimer.h"
|
||||
// Lua: mqtt:connect( host, port, secure, auto_reconnect, function(client), function(client, connect_return_code) )
|
||||
static int mqtt_socket_connect( lua_State* L )
|
||||
{
|
||||
|
@ -1114,6 +1115,9 @@ static int mqtt_socket_connect( lua_State* L )
|
|||
|
||||
os_timer_disarm(&mud->mqttTimer);
|
||||
os_timer_setfn(&mud->mqttTimer, (os_timer_func_t *)mqtt_socket_timer, mud);
|
||||
SWTIMER_REG_CB(mqtt_socket_timer, SWTIMER_RESUME);
|
||||
//I assume that mqtt_socket_timer connects to the mqtt server, but I'm not really sure what impact light_sleep will have on it.
|
||||
//My guess: If in doubt, resume the timer
|
||||
// timer started in socket_connect()
|
||||
|
||||
if((ipaddr.addr == IPADDR_NONE) && (c_memcmp(domain,"255.255.255.255",16) != 0))
|
||||
|
|
|
@ -78,7 +78,7 @@ static int node_deepsleep( lua_State* L )
|
|||
|
||||
|
||||
#ifdef PMSLEEP_ENABLE
|
||||
#include "pmSleep.h"
|
||||
#include "pm/pmSleep.h"
|
||||
|
||||
int node_sleep_resume_cb_ref= LUA_NOREF;
|
||||
void node_sleep_resume_cb(void)
|
||||
|
@ -91,6 +91,7 @@ void node_sleep_resume_cb(void)
|
|||
// Lua: node.sleep(table)
|
||||
static int node_sleep( lua_State* L )
|
||||
{
|
||||
#ifdef TIMER_SUSPEND_ENABLE
|
||||
pmSleep_INIT_CFG(cfg);
|
||||
cfg.sleep_mode=LIGHT_SLEEP_T;
|
||||
|
||||
|
@ -103,6 +104,10 @@ static int node_sleep( lua_State* L )
|
|||
|
||||
cfg.resume_cb_ptr = &node_sleep_resume_cb;
|
||||
pmSleep_suspend(&cfg);
|
||||
#else
|
||||
c_printf("\n The option \"timer_suspend_enable\" in \"app/include/user_config.h\" was disabled during FW build!\n");
|
||||
return luaL_error(L, "light sleep is unavailable");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#endif //PMSLEEP_ENABLE
|
||||
|
@ -463,14 +468,23 @@ static int node_stripdebug (lua_State *L) {
|
|||
// See legc.h and lecg.c.
|
||||
static int node_egc_setmode(lua_State* L) {
|
||||
unsigned mode = luaL_checkinteger(L, 1);
|
||||
unsigned limit = luaL_optinteger (L, 2, 0);
|
||||
int limit = luaL_optinteger (L, 2, 0);
|
||||
|
||||
luaL_argcheck(L, mode <= (EGC_ON_ALLOC_FAILURE | EGC_ON_MEM_LIMIT | EGC_ALWAYS), 1, "invalid mode");
|
||||
luaL_argcheck(L, !(mode & EGC_ON_MEM_LIMIT) || limit>0, 1, "limit must be non-zero");
|
||||
luaL_argcheck(L, !(mode & EGC_ON_MEM_LIMIT) || limit!=0, 1, "limit must be non-zero");
|
||||
|
||||
legc_set_mode( L, mode, limit );
|
||||
return 0;
|
||||
}
|
||||
|
||||
// totalallocated, estimatedused = node.egc.meminfo()
|
||||
static int node_egc_meminfo(lua_State *L) {
|
||||
global_State *g = G(L);
|
||||
lua_pushinteger(L, g->totalbytes);
|
||||
lua_pushinteger(L, g->estimate);
|
||||
return 2;
|
||||
}
|
||||
|
||||
//
|
||||
// Lua: osprint(true/false)
|
||||
// Allows you to turn on the native Espressif SDK printing
|
||||
|
@ -561,6 +575,7 @@ static int node_random (lua_State *L) {
|
|||
// Module function map
|
||||
|
||||
static const LUA_REG_TYPE node_egc_map[] = {
|
||||
{ LSTRKEY( "meminfo" ), LFUNCVAL( node_egc_meminfo ) },
|
||||
{ LSTRKEY( "setmode" ), LFUNCVAL( node_egc_setmode ) },
|
||||
{ LSTRKEY( "NOT_ACTIVE" ), LNUMVAL( EGC_NOT_ACTIVE ) },
|
||||
{ LSTRKEY( "ON_ALLOC_FAILURE" ), LNUMVAL( EGC_ON_ALLOC_FAILURE ) },
|
||||
|
|
|
@ -129,6 +129,8 @@ int platform_rotary_exists( unsigned int id )
|
|||
return (id < ROTARY_CHANNEL_COUNT);
|
||||
}
|
||||
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
// Lua: setup(id, phase_a, phase_b [, press])
|
||||
static int lrotary_setup( lua_State* L )
|
||||
{
|
||||
|
@ -152,7 +154,14 @@ static int lrotary_setup( lua_State* L )
|
|||
DATA *d = data[id];
|
||||
memset(d, 0, sizeof(*d));
|
||||
|
||||
d->id = id;
|
||||
|
||||
os_timer_setfn(&d->timer, lrotary_timer_done, (void *) d);
|
||||
SWTIMER_REG_CB(lrotary_timer_done, SWTIMER_RESUME);
|
||||
//lrotary_timer_done checks time elapsed since last event
|
||||
//My guess: Since proper functionality relies on some variables to be reset via timer callback and state would be invalid anyway.
|
||||
//It is probably best to resume this timer so it can reset it's state variables
|
||||
|
||||
|
||||
int i;
|
||||
for (i = 0; i < CALLBACK_COUNT; i++) {
|
||||
|
|
|
@ -319,6 +319,7 @@ static void sntp_handle_result(lua_State *L) {
|
|||
}
|
||||
}
|
||||
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
static void sntp_dosend ()
|
||||
{
|
||||
|
@ -326,6 +327,9 @@ static void sntp_dosend ()
|
|||
if (state->server_pos < 0) {
|
||||
os_timer_disarm(&state->timer);
|
||||
os_timer_setfn(&state->timer, on_timeout, NULL);
|
||||
SWTIMER_REG_CB(on_timeout, SWTIMER_RESUME);
|
||||
//The function on_timeout calls this function(sntp_dosend) again to handle time sync timeout.
|
||||
//My guess: Since the WiFi connection is restored after waking from light sleep, it would be possible to contact the SNTP server, So why not let it
|
||||
state->server_pos = 0;
|
||||
} else {
|
||||
++state->server_pos;
|
||||
|
@ -386,14 +390,13 @@ static void sntp_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
|
|||
if (ipaddr == NULL)
|
||||
{
|
||||
sntp_dbg("DNS Fail!\n");
|
||||
task_post_low(tasknumber, (uint32_t) name);
|
||||
}
|
||||
else
|
||||
{
|
||||
serverp[server_count] = *ipaddr;
|
||||
server_count++;
|
||||
task_post_low(tasknumber, SNTP_DOLOOKUPS_ID);
|
||||
}
|
||||
task_post_low(tasknumber, SNTP_DOLOOKUPS_ID);
|
||||
}
|
||||
|
||||
|
||||
|
@ -621,15 +624,26 @@ static void sntp_dolookups (lua_State *L) {
|
|||
// at the end, start the lookups. If we have already looked everything up,
|
||||
// then move straight to sending the packets.
|
||||
if (state->list_ref == LUA_NOREF) {
|
||||
sntp_dosend(L);
|
||||
sntp_dosend();
|
||||
return;
|
||||
}
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, state->list_ref);
|
||||
while (1) {
|
||||
int l;
|
||||
|
||||
if (lua_objlen(L, -1) <= state->lookup_pos) {
|
||||
// We reached the end
|
||||
sntp_dosend(L);
|
||||
if (server_count == 0) {
|
||||
// Oh dear -- no valid entries -- generate an error
|
||||
// This means that all the arguments are invalid. Just pick the first
|
||||
lua_rawgeti(L, -1, 1);
|
||||
const char *hostname = luaL_checklstring(L, -1, &l);
|
||||
handle_error(L, NTP_DNS_ERR, hostname);
|
||||
lua_pop(L, 1);
|
||||
} else {
|
||||
sntp_dosend();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -637,8 +651,6 @@ static void sntp_dolookups (lua_State *L) {
|
|||
|
||||
lua_rawgeti(L, -1, state->lookup_pos);
|
||||
|
||||
int l;
|
||||
|
||||
const char *hostname = luaL_checklstring(L, -1, &l);
|
||||
lua_pop(L, 1);
|
||||
|
||||
|
@ -700,6 +712,9 @@ static char *set_repeat_mode(lua_State *L, bool enable)
|
|||
lua_rawgeti(L, LUA_REGISTRYINDEX, state->list_ref);
|
||||
repeat->list_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
os_timer_setfn(&repeat->timer, on_long_timeout, NULL);
|
||||
SWTIMER_REG_CB(on_long_timeout, SWTIMER_RESUME);
|
||||
//The function on_long_timeout returns errors to the developer
|
||||
//My guess: Error reporting is a good thing, resume the timer.
|
||||
os_timer_arm(&repeat->timer, 1000 * 1000, 1);
|
||||
} else {
|
||||
if (repeat) {
|
||||
|
@ -806,7 +821,7 @@ static int sntp_sync (lua_State *L)
|
|||
goto good_ret;
|
||||
}
|
||||
|
||||
sntp_dosend (L);
|
||||
sntp_dosend ();
|
||||
|
||||
good_ret:
|
||||
if (!lua_isnoneornil(L, 4)) {
|
||||
|
@ -837,10 +852,8 @@ static void sntp_task(os_param_t param, uint8_t prio)
|
|||
sntp_handle_result(L);
|
||||
} else if (param == SNTP_DOLOOKUPS_ID) {
|
||||
sntp_dolookups(L);
|
||||
} else if (param >= 0 && param <= NTP_MAX_ERR_ID) {
|
||||
handle_error(L, param, NULL);
|
||||
} else {
|
||||
handle_error(L, NTP_DNS_ERR, (const char *) param);
|
||||
handle_error(L, param, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "mem.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "espconn.h"
|
||||
#include "sys/espconn_mbedtls.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ tmr.softwd(int)
|
|||
#include "platform.h"
|
||||
#include "c_types.h"
|
||||
#include "user_interface.h"
|
||||
#include "swTimer/swTimer.h"
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
#define TIMER_MODE_OFF 3
|
||||
#define TIMER_MODE_SINGLE 0
|
||||
|
@ -128,9 +128,9 @@ static int tmr_delay( lua_State* L ){
|
|||
sint32_t us = luaL_checkinteger(L, 1);
|
||||
if(us <= 0)
|
||||
return luaL_error(L, "wrong arg range");
|
||||
while(us >= 10000){
|
||||
us -= 10000;
|
||||
os_delay_us(10000);
|
||||
while(us >= 1000000){
|
||||
us -= 1000000;
|
||||
os_delay_us(1000000);
|
||||
system_soft_wdt_feed ();
|
||||
}
|
||||
if(us>0){
|
||||
|
@ -231,68 +231,23 @@ static int tmr_stop(lua_State* L){
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TIMER_SUSPEND
|
||||
#ifdef TIMER_SUSPEND_ENABLE
|
||||
|
||||
#define TMR_SUSPEND_REMOVED_MSG "This feature has been removed, we apologize for any inconvenience this may have caused."
|
||||
static int tmr_suspend(lua_State* L){
|
||||
timer_t tmr = tmr_get(L, 1);
|
||||
|
||||
if((tmr->mode & TIMER_IDLE_FLAG) == 1){
|
||||
return luaL_error(L, "timer not armed");
|
||||
}
|
||||
|
||||
int retval = swtmr_suspend(&tmr->os);
|
||||
|
||||
if(retval != SWTMR_OK){
|
||||
return luaL_error(L, swtmr_errorcode2str(retval));
|
||||
}
|
||||
else{
|
||||
lua_pushboolean(L, true);
|
||||
}
|
||||
|
||||
return 1;
|
||||
return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
|
||||
}
|
||||
|
||||
static int tmr_resume(lua_State* L){
|
||||
timer_t tmr = tmr_get(L, 1);
|
||||
|
||||
if(swtmr_suspended_test(&tmr->os) == FALSE){
|
||||
return luaL_error(L, "timer not suspended");
|
||||
}
|
||||
|
||||
int retval = swtmr_resume(&tmr->os);
|
||||
|
||||
if(retval != SWTMR_OK){
|
||||
return luaL_error(L, swtmr_errorcode2str(retval));
|
||||
}
|
||||
else{
|
||||
lua_pushboolean(L, true);
|
||||
}
|
||||
return 1;
|
||||
return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
|
||||
}
|
||||
|
||||
static int tmr_suspend_all (lua_State *L)
|
||||
{
|
||||
sint32 retval = swtmr_suspend(NULL);
|
||||
// lua_pushnumber(L, swtmr_suspend(NULL));
|
||||
if(retval!=SWTMR_OK){
|
||||
return luaL_error(L, swtmr_errorcode2str(retval));
|
||||
}
|
||||
else{
|
||||
lua_pushboolean(L, true);
|
||||
}
|
||||
return 1;
|
||||
static int tmr_suspend_all (lua_State *L){
|
||||
return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
|
||||
}
|
||||
|
||||
static int tmr_resume_all (lua_State *L)
|
||||
{
|
||||
sint32 retval = swtmr_resume(NULL);
|
||||
if(retval!=SWTMR_OK){
|
||||
return luaL_error(L, swtmr_errorcode2str(retval));
|
||||
}
|
||||
else{
|
||||
lua_pushboolean(L, true);
|
||||
}
|
||||
return 1;
|
||||
static int tmr_resume_all (lua_State *L){
|
||||
return luaL_error(L, TMR_SUSPEND_REMOVED_MSG);
|
||||
}
|
||||
|
||||
|
||||
|
@ -343,12 +298,7 @@ static int tmr_state(lua_State* L){
|
|||
|
||||
lua_pushboolean(L, (tmr->mode & TIMER_IDLE_FLAG) == 0);
|
||||
lua_pushinteger(L, tmr->mode & (~TIMER_IDLE_FLAG));
|
||||
#ifdef ENABLE_TIMER_SUSPEND
|
||||
lua_pushboolean(L, swtmr_suspended_test(&tmr->os));
|
||||
#else
|
||||
lua_pushnil(L);
|
||||
#endif
|
||||
return 3;
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*I left the led comments 'couse I don't know
|
||||
|
@ -454,7 +404,7 @@ static const LUA_REG_TYPE tmr_dyn_map[] = {
|
|||
{ LSTRKEY( "unregister" ), LFUNCVAL( tmr_unregister ) },
|
||||
{ LSTRKEY( "state" ), LFUNCVAL( tmr_state ) },
|
||||
{ LSTRKEY( "interval" ), LFUNCVAL( tmr_interval) },
|
||||
#ifdef ENABLE_TIMER_SUSPEND
|
||||
#ifdef TIMER_SUSPEND_ENABLE
|
||||
{ LSTRKEY( "suspend" ), LFUNCVAL( tmr_suspend ) },
|
||||
{ LSTRKEY( "resume" ), LFUNCVAL( tmr_resume ) },
|
||||
#endif
|
||||
|
@ -463,15 +413,6 @@ static const LUA_REG_TYPE tmr_dyn_map[] = {
|
|||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
#if defined(ENABLE_TIMER_SUSPEND) && defined(SWTMR_DEBUG)
|
||||
static const LUA_REG_TYPE tmr_dbg_map[] = {
|
||||
{ LSTRKEY( "printRegistry" ), LFUNCVAL( tmr_printRegistry ) },
|
||||
{ LSTRKEY( "printSuspended" ), LFUNCVAL( tmr_printSuspended ) },
|
||||
{ LSTRKEY( "printTimerlist" ), LFUNCVAL( tmr_printTimerlist ) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
#endif
|
||||
|
||||
static const LUA_REG_TYPE tmr_map[] = {
|
||||
{ LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) },
|
||||
{ LSTRKEY( "now" ), LFUNCVAL( tmr_now ) },
|
||||
|
@ -482,7 +423,7 @@ static const LUA_REG_TYPE tmr_map[] = {
|
|||
{ LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) },
|
||||
{ LSTRKEY( "start" ), LFUNCVAL( tmr_start ) },
|
||||
{ LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) },
|
||||
#ifdef ENABLE_TIMER_SUSPEND
|
||||
#ifdef TIMER_SUSPEND_ENABLE
|
||||
{ LSTRKEY( "suspend" ), LFUNCVAL( tmr_suspend ) },
|
||||
{ LSTRKEY( "suspend_all" ), LFUNCVAL( tmr_suspend_all ) },
|
||||
{ LSTRKEY( "resume" ), LFUNCVAL( tmr_resume ) },
|
||||
|
@ -492,15 +433,13 @@ static const LUA_REG_TYPE tmr_map[] = {
|
|||
{ LSTRKEY( "state" ), LFUNCVAL( tmr_state ) },
|
||||
{ LSTRKEY( "interval" ), LFUNCVAL( tmr_interval ) },
|
||||
{ LSTRKEY( "create" ), LFUNCVAL( tmr_create ) },
|
||||
#if defined(ENABLE_TIMER_SUSPEND) && defined(SWTMR_DEBUG)
|
||||
{ LSTRKEY( "debug" ), LROVAL( tmr_dbg_map ) },
|
||||
#endif
|
||||
{ LSTRKEY( "ALARM_SINGLE" ), LNUMVAL( TIMER_MODE_SINGLE ) },
|
||||
{ LSTRKEY( "ALARM_SEMI" ), LNUMVAL( TIMER_MODE_SEMI ) },
|
||||
{ LSTRKEY( "ALARM_AUTO" ), LNUMVAL( TIMER_MODE_AUTO ) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
#include "pm/swtimer.h"
|
||||
int luaopen_tmr( lua_State *L ){
|
||||
int i;
|
||||
|
||||
|
@ -510,16 +449,23 @@ int luaopen_tmr( lua_State *L ){
|
|||
alarm_timers[i].lua_ref = LUA_NOREF;
|
||||
alarm_timers[i].self_ref = LUA_REFNIL;
|
||||
alarm_timers[i].mode = TIMER_MODE_OFF;
|
||||
//improve boot speed by using ets_timer_disarm instead of os_timer_disarm to avoid timer registry maintenance call.
|
||||
ets_timer_disarm(&alarm_timers[i].os);
|
||||
os_timer_disarm(&alarm_timers[i].os);
|
||||
}
|
||||
last_rtc_time=system_get_rtc_time(); // Right now is time 0
|
||||
last_rtc_time_us=0;
|
||||
|
||||
//improve boot speed by using ets_timer_disarm instead of os_timer_disarm to avoid timer registry maintenance call.
|
||||
ets_timer_disarm(&rtc_timer);
|
||||
os_timer_disarm(&rtc_timer);
|
||||
os_timer_setfn(&rtc_timer, rtc_callback, NULL);
|
||||
os_timer_arm(&rtc_timer, 1000, 1);
|
||||
|
||||
SWTIMER_REG_CB(rtc_callback, SWTIMER_RESUME);
|
||||
//The function rtc_callback calls the a function that calibrates the SoftRTC for drift in the esp8266's clock.
|
||||
//My guess: after the duration of light_sleep there's bound to be some drift in the clock, so a calibration is due.
|
||||
SWTIMER_REG_CB(alarm_timer_common, SWTIMER_RESUME);
|
||||
//The function alarm_timer_common handles timers created by the developer via tmr.create().
|
||||
//No reason not to resume the timers, so resume em'.
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -215,6 +215,124 @@ static int wifi_exit_smart( lua_State* L )
|
|||
}
|
||||
#endif // WIFI_SMART_ENABLE
|
||||
|
||||
// Lua: wifi.getcountry()
|
||||
static int wifi_getcountry( lua_State* L ){
|
||||
|
||||
wifi_country_t cfg = {0};
|
||||
|
||||
|
||||
if(wifi_get_country(&cfg)){
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushstring(L, "country");
|
||||
lua_pushstring(L, cfg.cc);
|
||||
lua_rawset(L, -3);
|
||||
|
||||
lua_pushstring(L, "start_ch");
|
||||
lua_pushnumber(L, cfg.schan);
|
||||
lua_rawset(L, -3);
|
||||
|
||||
lua_pushstring(L, "end_ch");
|
||||
lua_pushnumber(L, (cfg.schan + cfg.nchan)-1);
|
||||
lua_rawset(L, -3);
|
||||
|
||||
lua_pushstring(L, "policy");
|
||||
lua_pushnumber(L, cfg.policy);
|
||||
lua_rawset(L, -3);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else{
|
||||
return luaL_error(L, "Unable to get country info");
|
||||
}
|
||||
}
|
||||
|
||||
// Lua: wifi.setcountry()
|
||||
static int wifi_setcountry( lua_State* L ){
|
||||
size_t len;
|
||||
uint8 start_ch;
|
||||
uint8 end_ch;
|
||||
wifi_country_t cfg = {0};
|
||||
|
||||
if(lua_istable(L, 1)){
|
||||
lua_getfield(L, 1, "country");
|
||||
if (!lua_isnil(L, -1)){
|
||||
if( lua_isstring(L, -1) ){
|
||||
const char *country_code = luaL_checklstring( L, -1, &len );
|
||||
luaL_argcheck(L, (len==2 && isalpha(country_code[0]) && isalpha(country_code[1])), 1, "country: country code must be 2 chars");
|
||||
c_memcpy(cfg.cc, country_code, len);
|
||||
if(cfg.cc[0] >= 0x61) cfg.cc[0]=cfg.cc[0]-32; //if lowercase change to uppercase
|
||||
if(cfg.cc[1] >= 0x61) cfg.cc[1]=cfg.cc[1]-32; //if lowercase change to uppercase
|
||||
}
|
||||
else
|
||||
return luaL_argerror( L, 1, "country: must be string" );
|
||||
}
|
||||
else{
|
||||
cfg.cc[0]='C';
|
||||
cfg.cc[1]='N';
|
||||
cfg.cc[2]=0x00;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 1, "start_ch");
|
||||
if (!lua_isnil(L, -1)){
|
||||
if(lua_isnumber(L, -1)){
|
||||
start_ch = (uint8)luaL_checknumber(L, -1);
|
||||
luaL_argcheck(L, (start_ch >= 1 && start_ch <= 14), 1, "start_ch: Range:1-14");
|
||||
cfg.schan = start_ch;
|
||||
}
|
||||
else
|
||||
luaL_argerror(L, 1, "start_ch: must be a number");
|
||||
}
|
||||
else
|
||||
cfg.schan = 1;
|
||||
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 1, "end_ch");
|
||||
if (!lua_isnil(L, -1)){
|
||||
if(lua_isnumber(L, -1)){
|
||||
end_ch = (uint8)luaL_checknumber(L, -1);
|
||||
luaL_argcheck(L, (end_ch >= 1 && end_ch <= 14), 1, "end_ch: Range:1-14");
|
||||
luaL_argcheck(L, (end_ch >= cfg.schan), 1, "end_ch: can't be less than start_ch");
|
||||
cfg.nchan = (end_ch-cfg.schan)+1; //cfg.nchan must equal total number of channels
|
||||
}
|
||||
else
|
||||
luaL_argerror(L, 1, "end_ch: must be a number");
|
||||
}
|
||||
else
|
||||
cfg.nchan = (13-cfg.schan)+1;
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 1, "policy");
|
||||
if (!lua_isnil(L, -1)){
|
||||
if(lua_isnumber(L, -1)){
|
||||
uint8 policy = (uint8)luaL_checknumber(L, -1);
|
||||
luaL_argcheck(L, (policy == WIFI_COUNTRY_POLICY_AUTO || policy == WIFI_COUNTRY_POLICY_MANUAL), 1, "policy: must be 0 or 1");
|
||||
cfg.policy = policy;
|
||||
}
|
||||
else
|
||||
luaL_argerror(L, 1, "policy: must be a number");
|
||||
}
|
||||
else
|
||||
cfg.policy = WIFI_COUNTRY_POLICY_AUTO;
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pop(L, 1); //pop table from stack
|
||||
|
||||
bool retval = wifi_set_country(&cfg);
|
||||
WIFI_DBG("\n country_cfg:\tcc:\"%s\"\tschan:%d\tnchan:%d\tpolicy:%d\n", cfg.cc, cfg.schan, cfg.nchan, cfg.policy);
|
||||
|
||||
if (retval==1)
|
||||
lua_pushboolean(L, true);
|
||||
else
|
||||
return luaL_error(L, "Unable to set country info");
|
||||
}
|
||||
else
|
||||
return luaL_argerror(L, 1, "Table not found!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Lua: wifi.setmode(mode, save_to_flash)
|
||||
static int wifi_setmode( lua_State* L )
|
||||
{
|
||||
|
@ -311,7 +429,7 @@ static int wifi_setmaxtxpower( lua_State* L )
|
|||
|
||||
#ifdef PMSLEEP_ENABLE
|
||||
/* Begin WiFi suspend functions*/
|
||||
#include "pmSleep.h"
|
||||
#include <pm/pmSleep.h>
|
||||
|
||||
static int wifi_resume_cb_ref = LUA_NOREF; // Holds resume callback reference
|
||||
static int wifi_suspend_cb_ref = LUA_NOREF; // Holds suspend callback reference
|
||||
|
@ -393,6 +511,19 @@ static int wifi_resume(lua_State* L)
|
|||
}
|
||||
|
||||
/* End WiFi suspend functions*/
|
||||
#else
|
||||
static char *susp_note_str = "\n The option \"pmsleep_enable\" in \"app/include/user_config.h\" was disabled during FW build!\n";
|
||||
static char *susp_unavailable_str = "wifi.suspend is unavailable";
|
||||
|
||||
static int wifi_suspend(lua_State* L){
|
||||
c_sprintf("%s", susp_note_str);
|
||||
return luaL_error(L, susp_unavailable_str);
|
||||
}
|
||||
|
||||
static int wifi_resume(lua_State* L){
|
||||
c_sprintf("%s", susp_note_str);
|
||||
return luaL_error(L, susp_unavailable_str);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Lua: wifi.nullmodesleep()
|
||||
|
@ -722,10 +853,7 @@ static int wifi_station_clear_config ( lua_State* L )
|
|||
bool auto_connect=true;
|
||||
bool save_to_flash=true;
|
||||
|
||||
memset(sta_conf.ssid, 0, sizeof(sta_conf.ssid));
|
||||
memset(sta_conf.password, 0, sizeof(sta_conf.password));
|
||||
memset(sta_conf.bssid, 0, sizeof(sta_conf.bssid));
|
||||
sta_conf.bssid_set=0;
|
||||
memset(&sta_conf, 0, sizeof(sta_conf));
|
||||
|
||||
wifi_station_disconnect();
|
||||
|
||||
|
@ -753,10 +881,9 @@ static int wifi_station_config( lua_State* L )
|
|||
bool save_to_flash=true;
|
||||
size_t sl, pl, ml;
|
||||
|
||||
memset(sta_conf.ssid, 0, sizeof(sta_conf.ssid));
|
||||
memset(sta_conf.password, 0, sizeof(sta_conf.password));
|
||||
memset(sta_conf.bssid, 0, sizeof(sta_conf.bssid));
|
||||
sta_conf.bssid_set=0;
|
||||
memset(&sta_conf, 0, sizeof(sta_conf));
|
||||
sta_conf.threshold.rssi = -127;
|
||||
sta_conf.threshold.authmode = AUTH_OPEN;
|
||||
|
||||
if(lua_istable(L, 1))
|
||||
{
|
||||
|
@ -849,7 +976,7 @@ static int wifi_station_config( lua_State* L )
|
|||
|
||||
lua_State* L_temp = NULL;
|
||||
|
||||
lua_getfield(L, 1, "connected_cb");
|
||||
lua_getfield(L, 1, "connect_cb");
|
||||
if (!lua_isnil(L, -1))
|
||||
{
|
||||
if (lua_isfunction(L, -1))
|
||||
|
@ -862,12 +989,12 @@ static int wifi_station_config( lua_State* L )
|
|||
}
|
||||
else
|
||||
{
|
||||
return luaL_argerror(L, 1, "connected_cb:not function");
|
||||
return luaL_argerror(L, 1, "connect_cb:not function");
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 1, "disconnected_cb");
|
||||
lua_getfield(L, 1, "disconnect_cb");
|
||||
if (!lua_isnil(L, -1))
|
||||
{
|
||||
if (lua_isfunction(L, -1))
|
||||
|
@ -880,7 +1007,7 @@ static int wifi_station_config( lua_State* L )
|
|||
}
|
||||
else
|
||||
{
|
||||
return luaL_argerror(L, 1, "disconnected_cb:not function");
|
||||
return luaL_argerror(L, 1, "disconnect_cb:not function");
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
@ -1032,6 +1159,8 @@ static int wifi_station_listap( lua_State* L )
|
|||
return luaL_error( L, "Can't list ap in SOFTAP mode" );
|
||||
}
|
||||
struct scan_config scan_cfg;
|
||||
memset(&scan_cfg, 0, sizeof(scan_cfg));
|
||||
|
||||
getap_output_format=0;
|
||||
|
||||
if (lua_type(L, 1)==LUA_TTABLE)
|
||||
|
@ -1061,10 +1190,6 @@ static int wifi_station_listap( lua_State* L )
|
|||
return luaL_error( L, "wrong arg type" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_cfg.ssid=NULL;
|
||||
}
|
||||
|
||||
lua_getfield(L, 1, "bssid");
|
||||
if (!lua_isnil(L, -1)) /* found? */
|
||||
|
@ -1085,10 +1210,6 @@ static int wifi_station_listap( lua_State* L )
|
|||
return luaL_error( L, "wrong arg type" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_cfg.bssid=NULL;
|
||||
}
|
||||
|
||||
|
||||
lua_getfield(L, 1, "channel");
|
||||
|
@ -1107,10 +1228,6 @@ static int wifi_station_listap( lua_State* L )
|
|||
return luaL_error( L, "wrong arg type" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_cfg.channel=0;
|
||||
}
|
||||
|
||||
lua_getfield(L, 1, "show_hidden");
|
||||
if (!lua_isnil(L, -1)) /* found? */
|
||||
|
@ -1129,10 +1246,6 @@ static int wifi_station_listap( lua_State* L )
|
|||
return luaL_error( L, "wrong arg type" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
scan_cfg.show_hidden=0;
|
||||
}
|
||||
|
||||
if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TLIGHTFUNCTION)
|
||||
{
|
||||
|
@ -1805,13 +1918,13 @@ static const LUA_REG_TYPE wifi_map[] = {
|
|||
{ LSTRKEY( "getmode" ), LFUNCVAL( wifi_getmode ) },
|
||||
{ LSTRKEY( "getdefaultmode" ), LFUNCVAL( wifi_getdefaultmode ) },
|
||||
{ LSTRKEY( "getchannel" ), LFUNCVAL( wifi_getchannel ) },
|
||||
{ LSTRKEY( "getcountry" ), LFUNCVAL( wifi_getcountry ) },
|
||||
{ LSTRKEY( "setcountry" ), LFUNCVAL( wifi_setcountry ) },
|
||||
{ LSTRKEY( "setphymode" ), LFUNCVAL( wifi_setphymode ) },
|
||||
{ LSTRKEY( "getphymode" ), LFUNCVAL( wifi_getphymode ) },
|
||||
{ LSTRKEY( "setmaxtxpower" ), LFUNCVAL( wifi_setmaxtxpower ) },
|
||||
#ifdef PMSLEEP_ENABLE
|
||||
{ LSTRKEY( "suspend" ), LFUNCVAL( wifi_suspend ) },
|
||||
{ LSTRKEY( "resume" ), LFUNCVAL( wifi_resume ) },
|
||||
#endif
|
||||
{ LSTRKEY( "nullmodesleep" ), LFUNCVAL( wifi_null_mode_auto_sleep ) },
|
||||
#ifdef WIFI_SMART_ENABLE
|
||||
{ LSTRKEY( "startsmart" ), LFUNCVAL( wifi_start_smart ) },
|
||||
|
@ -1853,6 +1966,9 @@ static const LUA_REG_TYPE wifi_map[] = {
|
|||
{ LSTRKEY( "STA_FAIL" ), LNUMVAL( STATION_CONNECT_FAIL ) },
|
||||
{ LSTRKEY( "STA_GOTIP" ), LNUMVAL( STATION_GOT_IP ) },
|
||||
|
||||
{ LSTRKEY( "COUNTRY_AUTO" ), LNUMVAL( WIFI_COUNTRY_POLICY_AUTO ) },
|
||||
{ LSTRKEY( "COUNTRY_MANUAL" ), LNUMVAL( WIFI_COUNTRY_POLICY_MANUAL ) },
|
||||
|
||||
{ LSTRKEY( "__metatable" ), LROVAL( wifi_map ) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#include "user_interface.h"
|
||||
#include "driver/uart.h"
|
||||
#include "osapi.h"
|
||||
#include "swTimer/swTimer.h"
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
#include "ws2812.h"
|
||||
#include "color_utils.h"
|
||||
|
|
|
@ -1056,6 +1056,7 @@ mdns_dup_info(const struct nodemcu_mdns_info *info) {
|
|||
return result;
|
||||
}
|
||||
|
||||
#include "pm/swtimer.h"
|
||||
/**
|
||||
* Initialize the resolver: set up the UDP pcb and configure the default server
|
||||
* (NEW IP).
|
||||
|
@ -1130,6 +1131,9 @@ nodemcu_mdns_init(struct nodemcu_mdns_info *info) {
|
|||
//MDNS_DBG("About to start timer\n");
|
||||
os_timer_disarm(&mdns_timer);
|
||||
os_timer_setfn(&mdns_timer, (os_timer_func_t *)mdns_reg,ms_info);
|
||||
SWTIMER_REG_CB(mdns_reg, SWTIMER_RESUME);
|
||||
//the function mdns_reg registers the mdns device on the network
|
||||
//My guess: Since wifi connection is restored after waking from light_sleep, the related timer would have no problem resuming it's normal function.
|
||||
os_timer_arm(&mdns_timer, 1000 * 280, 1);
|
||||
/* kick off the first one right away */
|
||||
mdns_reg_handler_restart();
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#include "pmSleep.h"
|
||||
#include <pm/pmSleep.h>
|
||||
#ifdef PMSLEEP_ENABLE
|
||||
#define STRINGIFY_VAL(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_VAL(x)
|
||||
|
||||
//TODO: figure out why timed light_sleep doesn't work
|
||||
|
||||
//holds duration error string
|
||||
//uint32 PMSLEEP_SLEEP_MAX_TIME=FPM_SLEEP_MAX_TIME-1;
|
||||
const char *PMSLEEP_DURATION_ERR_STR="duration: 0 or "STRINGIFY(PMSLEEP_SLEEP_MIN_TIME)"-"STRINGIFY(PMSLEEP_SLEEP_MAX_TIME)" us";
|
||||
|
@ -28,17 +30,18 @@ static void wifi_suspended_timer_cb(int arg);
|
|||
|
||||
/* INTERNAL FUNCTIONS */
|
||||
|
||||
#include "swTimer/swTimer.h"
|
||||
static void suspend_all_timers(void){
|
||||
#ifdef ENABLE_TIMER_SUSPEND
|
||||
swtmr_suspend(NULL);
|
||||
#ifdef TIMER_SUSPEND_ENABLE
|
||||
extern void swtmr_suspend_timers();
|
||||
swtmr_suspend_timers();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
static void resume_all_timers(void){
|
||||
#ifdef ENABLE_TIMER_SUSPEND
|
||||
swtmr_resume(NULL);
|
||||
#ifdef TIMER_SUSPEND_ENABLE
|
||||
extern void swtmr_resume_timers();
|
||||
swtmr_resume_timers();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
@ -49,7 +52,7 @@ static void null_mode_check_timer_cb(void* arg){
|
|||
if(current_config.sleep_mode == LIGHT_SLEEP_T){
|
||||
if((READ_PERI_REG(UART_STATUS(0)) & (UART_TXFIFO_CNT<<UART_TXFIFO_CNT_S)) == 0 &&
|
||||
(READ_PERI_REG(UART_STATUS(1)) & (UART_TXFIFO_CNT<<UART_TXFIFO_CNT_S)) == 0){
|
||||
ets_timer_disarm(&null_mode_check_timer);
|
||||
os_timer_disarm(&null_mode_check_timer);
|
||||
suspend_all_timers();
|
||||
//Ensure UART 0/1 TX FIFO is clear
|
||||
SET_PERI_REG_MASK(UART_CONF0(0), UART_TXFIFO_RST);//RESET FIFO
|
||||
|
@ -71,6 +74,8 @@ static void null_mode_check_timer_cb(void* arg){
|
|||
PMSLEEP_DBG("wifi_fpm_do_sleep success, starting wifi_suspend_test timer");
|
||||
os_timer_disarm(&wifi_suspended_test_timer);
|
||||
os_timer_setfn(&wifi_suspended_test_timer, (os_timer_func_t*)wifi_suspended_timer_cb, NULL);
|
||||
//The callback wifi_suspended_timer_cb detects when the esp8266 has successfully entered modem_sleep and executes the developer's suspend_cb.
|
||||
//Since this timer is only used in modem_sleep and will never be active outside of modem_sleep, it is unnecessary to register the cb with SWTIMER_REG_CB.
|
||||
os_timer_arm(&wifi_suspended_test_timer, 1, 1);
|
||||
}
|
||||
else{ // This should never happen. if it does, return the value for error reporting
|
||||
|
@ -78,7 +83,7 @@ static void null_mode_check_timer_cb(void* arg){
|
|||
PMSLEEP_ERR("wifi_fpm_do_sleep returned %d", retval_wifi_fpm_do_sleep);
|
||||
}
|
||||
}
|
||||
ets_timer_disarm(&null_mode_check_timer);
|
||||
os_timer_disarm(&null_mode_check_timer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -173,24 +178,24 @@ uint8 pmSleep_get_state(void){
|
|||
int pmSleep_parse_table_lua( lua_State* L, int table_idx, pmSleep_param_t *cfg, int *suspend_lua_cb_ref, int *resume_lua_cb_ref){
|
||||
lua_Integer Linteger_tmp = 0;
|
||||
|
||||
lua_getfield(L, table_idx, "duration");
|
||||
if( !lua_isnil(L, -1) ){ /* found? */
|
||||
if( lua_isnumber(L, -1) ){
|
||||
lua_Integer Linteger=luaL_checkinteger(L, -1);
|
||||
luaL_argcheck(L,(((Linteger >= PMSLEEP_SLEEP_MIN_TIME) && (Linteger <= PMSLEEP_SLEEP_MAX_TIME)) ||
|
||||
(Linteger == 0)), table_idx, PMSLEEP_DURATION_ERR_STR);
|
||||
cfg->sleep_duration = (uint32)Linteger; // Get suspend duration
|
||||
if( cfg->sleep_mode == MODEM_SLEEP_T ){ //WiFi suspend
|
||||
lua_getfield(L, table_idx, "duration");
|
||||
if( !lua_isnil(L, -1) ){ /* found? */
|
||||
if( lua_isnumber(L, -1) ){
|
||||
lua_Integer Linteger=luaL_checkinteger(L, -1);
|
||||
luaL_argcheck(L,(((Linteger >= PMSLEEP_SLEEP_MIN_TIME) && (Linteger <= PMSLEEP_SLEEP_MAX_TIME)) ||
|
||||
(Linteger == 0)), table_idx, PMSLEEP_DURATION_ERR_STR);
|
||||
cfg->sleep_duration = (uint32)Linteger; // Get suspend duration
|
||||
}
|
||||
else{
|
||||
return luaL_argerror( L, table_idx, "duration: must be number" );
|
||||
}
|
||||
}
|
||||
else{
|
||||
return luaL_argerror( L, table_idx, "duration: must be number" );
|
||||
return luaL_argerror( L, table_idx, PMSLEEP_DURATION_ERR_STR );
|
||||
}
|
||||
}
|
||||
else{
|
||||
return luaL_argerror( L, table_idx, PMSLEEP_DURATION_ERR_STR );
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
if( cfg->sleep_mode == MODEM_SLEEP_T ){ //WiFi suspend
|
||||
lua_getfield(L, table_idx, "suspend_cb");
|
||||
if( !lua_isnil(L, -1) ){ /* found? */
|
||||
if( lua_isfunction(L, -1) ){
|
||||
|
@ -204,7 +209,7 @@ int pmSleep_parse_table_lua( lua_State* L, int table_idx, pmSleep_param_t *cfg,
|
|||
lua_pop(L, 1);
|
||||
}
|
||||
else if (cfg->sleep_mode == LIGHT_SLEEP_T){ //CPU suspend
|
||||
#ifdef ENABLE_TIMER_SUSPEND
|
||||
#ifdef TIMER_SUSPEND_ENABLE
|
||||
lua_getfield(L, table_idx, "wake_pin");
|
||||
if( !lua_isnil(L, -1) ){ /* found? */
|
||||
if( lua_isnumber(L, -1) ){
|
||||
|
@ -216,9 +221,11 @@ int pmSleep_parse_table_lua( lua_State* L, int table_idx, pmSleep_param_t *cfg,
|
|||
return luaL_argerror( L, table_idx, "wake_pin: must be number" );
|
||||
}
|
||||
}
|
||||
else if(cfg->sleep_duration == 0){
|
||||
return luaL_argerror( L, table_idx, "wake_pin: must specify pin if sleep duration is indefinite" );
|
||||
}
|
||||
else{
|
||||
return luaL_argerror( L, table_idx, "wake_pin: must specify pin" );
|
||||
// else if(cfg->sleep_duration == 0){
|
||||
// return luaL_argerror( L, table_idx, "wake_pin: must specify pin if sleep duration is indefinite" );
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, table_idx, "int_type");
|
||||
|
@ -300,7 +307,7 @@ void pmSleep_suspend(pmSleep_param_t *cfg){
|
|||
PMSLEEP_DBG("START");
|
||||
|
||||
lua_State* L = lua_getstate();
|
||||
#ifndef ENABLE_TIMER_SUSPEND
|
||||
#ifndef TIMER_SUSPEND_ENABLE
|
||||
if(cfg->sleep_mode == LIGHT_SLEEP_T){
|
||||
luaL_error(L, "timer suspend API is disabled, light sleep unavailable");
|
||||
return;
|
||||
|
@ -336,7 +343,7 @@ void pmSleep_suspend(pmSleep_param_t *cfg){
|
|||
wifi_fpm_open(); // Enable force sleep API
|
||||
|
||||
if (cfg->sleep_mode == LIGHT_SLEEP_T){
|
||||
#ifdef ENABLE_TIMER_SUSPEND
|
||||
#ifdef TIMER_SUSPEND_ENABLE
|
||||
if(platform_gpio_exists(cfg->wake_pin) && cfg->wake_pin > 0){
|
||||
PMSLEEP_DBG("Wake-up pin is %d\t interrupt type is %d", cfg->wake_pin, cfg->int_type);
|
||||
|
||||
|
@ -366,10 +373,11 @@ void pmSleep_suspend(pmSleep_param_t *cfg){
|
|||
c_memcpy(¤t_config, cfg, sizeof(pmSleep_param_t));
|
||||
PMSLEEP_DBG("sleep duration is %d", current_config.sleep_duration);
|
||||
|
||||
//this timer intentionally bypasses the swtimer timer registration process
|
||||
ets_timer_disarm(&null_mode_check_timer);
|
||||
ets_timer_setfn(&null_mode_check_timer, null_mode_check_timer_cb, false);
|
||||
ets_timer_arm_new(&null_mode_check_timer, 1, 1, 1);
|
||||
os_timer_disarm(&null_mode_check_timer);
|
||||
os_timer_setfn(&null_mode_check_timer, null_mode_check_timer_cb, false);
|
||||
//The function null_mode_check_timer_cb checks that the esp8266 has successfully changed the opmode to NULL_MODE prior to entering LIGHT_SLEEP
|
||||
//This callback doesn't need to be registered with SWTIMER_REG_CB since the timer will have terminated before entering LIGHT_SLEEP
|
||||
os_timer_arm(&null_mode_check_timer, 1, 1);
|
||||
}
|
||||
else{
|
||||
PMSLEEP_ERR("opmode change fail");
|
||||
|
|
|
@ -0,0 +1,545 @@
|
|||
/* swTimer.c SDK timer suspend API
|
||||
*
|
||||
* SDK software timer API info:
|
||||
*
|
||||
* The SDK software timer uses a linked list called `os_timer_t* timer_list` to keep track of
|
||||
* all currently armed timers.
|
||||
*
|
||||
* The SDK software timer API executes in a task. The priority of this task in relation to the
|
||||
* application level tasks is unknown (at time of writing).
|
||||
*
|
||||
*
|
||||
* To determine when a timer's callback should be executed, the respective timer's `timer_expire`
|
||||
* variable is compared to the hardware counter(FRC2), then, if the timer's `timer_expire` is
|
||||
* less than the current FRC2 count, the timer's callback is fired.
|
||||
*
|
||||
* The timers in this list are organized in an ascending order starting with the timer
|
||||
* with the lowest timer_expire.
|
||||
*
|
||||
* When a timer expires that has a timer_period greater than 0, timer_expire is changed to
|
||||
* current FRC2 + timer_period, then the timer is inserted back in to the list in the correct position.
|
||||
*
|
||||
* when using millisecond(default) timers, FRC2 resolution is 312.5 ticks per millisecond.
|
||||
*
|
||||
*
|
||||
* TIMER SUSPEND API INFO:
|
||||
*
|
||||
* Timer suspension is achieved by first finding any non-SDK timers by comparing the timer function callback pointer
|
||||
* of each timer in "timer_list" to a list of registered timer callback pointers stored in the Lua registry.
|
||||
* If a timer with a corresponding registered callback pointer is found, the timer's timer_expire field is is compared
|
||||
* to the current FRC2 count and the difference is saved along with the other timer parameters to temporary variables.
|
||||
* The timer is then disarmed and the parameters are copied back, the timer pointer is then
|
||||
* added to a separate linked list of which the head pointer is stored as a lightuserdata in the lua registry.
|
||||
*
|
||||
* Resuming the timers is achieved by first retrieving the lightuserdata holding the suspended timer list head pointer.
|
||||
* Then, starting with the beginning of the list the current FRC2 count is added back to the timer's timer_expire, then
|
||||
* the timer is manually added back to "timer_list" in an ascending order.
|
||||
* Once there are no more suspended timers, the function returns
|
||||
*
|
||||
*
|
||||
*/#include "module.h"
|
||||
#include "lauxlib.h"
|
||||
#include "platform.h"
|
||||
|
||||
#include "user_interface.h"
|
||||
#include "user_modules.h"
|
||||
|
||||
#include "c_string.h"
|
||||
#include "c_stdlib.h"
|
||||
#include "ctype.h"
|
||||
|
||||
#include "c_types.h"
|
||||
|
||||
//#define SWTMR_DEBUG
|
||||
#if !defined(SWTMR_DBG) && defined(LUA_USE_MODULES_SWTMR_DBG)
|
||||
#define SWTMR_DEBUG
|
||||
#endif
|
||||
|
||||
//this section specifies which lua registry to use. LUA_GLOBALSINDEX or LUA_REGISTRYINDEX
|
||||
#ifdef SWTMR_DEBUG
|
||||
#define SWTMR_DBG(fmt, ...) c_printf("\n SWTMR_DBG(%s): "fmt"\n", __FUNCTION__, ##__VA_ARGS__)
|
||||
#define L_REGISTRY LUA_GLOBALSINDEX
|
||||
#define CB_LIST_STR "timer_cb_ptrs"
|
||||
#define SUSP_LIST_STR "suspended_tmr_LL_head"
|
||||
#else
|
||||
#define SWTMR_DBG(...)
|
||||
#define L_REGISTRY LUA_REGISTRYINDEX
|
||||
#define CB_LIST_STR "cb"
|
||||
#define SUSP_LIST_STR "st"
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct tmr_cb_queue{
|
||||
os_timer_func_t *tmr_cb_ptr;
|
||||
uint8 suspend_policy;
|
||||
struct tmr_cb_queue * next;
|
||||
}tmr_cb_queue_t;
|
||||
|
||||
typedef struct cb_registry_item{
|
||||
os_timer_func_t *tmr_cb_ptr;
|
||||
uint8 suspend_policy;
|
||||
}cb_registry_item_t;
|
||||
|
||||
|
||||
/* Internal variables */
|
||||
static tmr_cb_queue_t* register_queue = NULL;
|
||||
static task_handle_t cb_register_task_id = NULL; //variable to hold task id for task handler(process_cb_register_queue)
|
||||
|
||||
/* Function declarations */
|
||||
//void swtmr_cb_register(void* timer_cb_ptr, uint8 resume_policy);
|
||||
static void add_to_reg_queue(void* timer_cb_ptr, uint8 suspend_policy);
|
||||
static void process_cb_register_queue(task_param_t param, uint8 priority);
|
||||
|
||||
|
||||
#ifdef SWTMR_DEBUG
|
||||
#define push_swtmr_registry_key(L) lua_pushstring(L, "SWTMR_registry_key")
|
||||
#else
|
||||
#define push_swtmr_registry_key(L) lua_pushlightuserdata(L, ®ister_queue);
|
||||
#endif
|
||||
|
||||
#include <pm/swtimer.h>
|
||||
|
||||
void swtmr_suspend_timers(){
|
||||
lua_State* L = lua_getstate();
|
||||
|
||||
//get swtimer table
|
||||
push_swtmr_registry_key(L);
|
||||
lua_rawget(L, L_REGISTRY);
|
||||
|
||||
//get cb_list table
|
||||
lua_pushstring(L, CB_LIST_STR);
|
||||
lua_rawget(L, -2);
|
||||
|
||||
//check for existence of the swtimer table and the cb_list table, return if not found
|
||||
if(!lua_istable(L, -2) || !lua_istable(L, -1)){
|
||||
// not necessarily an error maybe there are legitimately no timers to suspend
|
||||
lua_pop(L, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
os_timer_t* suspended_timer_list_head = NULL;
|
||||
os_timer_t* suspended_timer_list_tail = NULL;
|
||||
|
||||
//get suspended_timer_list table
|
||||
lua_pushstring(L, SUSP_LIST_STR);
|
||||
lua_rawget(L, -3);
|
||||
|
||||
//if suspended_timer_list exists, find tail of list
|
||||
if(lua_isuserdata(L, -1)){
|
||||
suspended_timer_list_head = suspended_timer_list_tail = lua_touserdata(L, -1);
|
||||
while(suspended_timer_list_tail->timer_next != NULL){
|
||||
suspended_timer_list_tail = suspended_timer_list_tail->timer_next;
|
||||
}
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
|
||||
//get length of lua table containing the callback pointers
|
||||
size_t registered_cb_qty = lua_objlen(L, -1);
|
||||
|
||||
//allocate a temporary array to hold the list of callback pointers
|
||||
cb_registry_item_t** cb_reg_array = c_zalloc(sizeof(cb_registry_item_t*)*registered_cb_qty);
|
||||
if(!cb_reg_array){
|
||||
luaL_error(L, "%s: unable to suspend timers, out of memory!", __func__);
|
||||
return;
|
||||
}
|
||||
uint8 index = 0;
|
||||
|
||||
//convert lua table cb_list to c array
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L, -2) != 0){
|
||||
if(lua_isuserdata(L, -1)){
|
||||
cb_reg_array[index] = lua_touserdata(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
index++;
|
||||
}
|
||||
|
||||
//the cb_list table is no longer needed, pop it from the stack
|
||||
lua_pop(L, 1);
|
||||
|
||||
volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS);
|
||||
|
||||
os_timer_t* timer_ptr = timer_list;
|
||||
|
||||
uint32 expire_temp = 0;
|
||||
uint32 period_temp = 0;
|
||||
void* arg_temp = NULL;
|
||||
|
||||
/* In this section, the SDK's timer_list is traversed to find any timers that have a registered callback pointer.
|
||||
* If a registered callback is found, the timer is suspended by saving the difference
|
||||
* between frc2_count and timer_expire then the timer is disarmed and placed into suspended_timer_list
|
||||
* so it can later be resumed.
|
||||
*/
|
||||
while(timer_ptr != NULL){
|
||||
os_timer_t* next_timer = (os_timer_t*)0xffffffff;
|
||||
for(int i = 0; i < registered_cb_qty; i++){
|
||||
if(timer_ptr->timer_func == cb_reg_array[i]->tmr_cb_ptr){
|
||||
|
||||
//current timer will be suspended, next timer's pointer will be needed to continue processing timer_list
|
||||
next_timer = timer_ptr->timer_next;
|
||||
|
||||
//store timer parameters temporarily so the timer can be disarmed
|
||||
if(timer_ptr->timer_expire < frc2_count)
|
||||
expire_temp = 2; // 16 us in ticks (1 tick = ~3.2 us) (arbitrarily chosen value)
|
||||
else
|
||||
expire_temp = timer_ptr->timer_expire - frc2_count;
|
||||
period_temp = timer_ptr->timer_period;
|
||||
arg_temp = timer_ptr->timer_arg;
|
||||
|
||||
if(timer_ptr->timer_period == 0 && cb_reg_array[i]->suspend_policy == SWTIMER_RESTART){
|
||||
SWTMR_DBG("Warning: suspend_policy(RESTART) is not compatible with single-shot timer(%p), changing suspend_policy to (RESUME)", timer_ptr);
|
||||
cb_reg_array[i]->suspend_policy = SWTIMER_RESUME;
|
||||
}
|
||||
|
||||
//remove the timer from timer_list so we don't have to.
|
||||
os_timer_disarm(timer_ptr);
|
||||
|
||||
timer_ptr->timer_next = NULL;
|
||||
|
||||
//this section determines timer behavior on resume
|
||||
if(cb_reg_array[i]->suspend_policy == SWTIMER_DROP){
|
||||
SWTMR_DBG("timer(%p) was disarmed and will not be resumed", timer_ptr);
|
||||
}
|
||||
else if(cb_reg_array[i]->suspend_policy == SWTIMER_IMMEDIATE){
|
||||
timer_ptr->timer_expire = 1;
|
||||
SWTMR_DBG("timer(%p) will fire immediately on resume", timer_ptr);
|
||||
}
|
||||
else if(cb_reg_array[i]->suspend_policy == SWTIMER_RESTART){
|
||||
timer_ptr->timer_expire = period_temp;
|
||||
SWTMR_DBG("timer(%p) will be restarted on resume", timer_ptr);
|
||||
}
|
||||
else{
|
||||
timer_ptr->timer_expire = expire_temp;
|
||||
SWTMR_DBG("timer(%p) will be resumed with remaining time", timer_ptr);
|
||||
}
|
||||
|
||||
if(cb_reg_array[i]->suspend_policy != SWTIMER_DROP){
|
||||
timer_ptr->timer_period = period_temp;
|
||||
timer_ptr->timer_func = cb_reg_array[i]->tmr_cb_ptr;
|
||||
timer_ptr->timer_arg = arg_temp;
|
||||
|
||||
//add timer to suspended_timer_list
|
||||
if(suspended_timer_list_head == NULL){
|
||||
suspended_timer_list_head = timer_ptr;
|
||||
suspended_timer_list_tail = timer_ptr;
|
||||
}
|
||||
else{
|
||||
suspended_timer_list_tail->timer_next = timer_ptr;
|
||||
suspended_timer_list_tail = timer_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if timer was suspended, timer_ptr->timer_next is invalid, use next_timer instead.
|
||||
if(next_timer != (os_timer_t*)0xffffffff){
|
||||
timer_ptr = next_timer;
|
||||
}
|
||||
else{
|
||||
timer_ptr = timer_ptr->timer_next;
|
||||
}
|
||||
}
|
||||
|
||||
//tmr_cb_ptr_array is no longer needed.
|
||||
c_free(cb_reg_array);
|
||||
|
||||
//add suspended_timer_list pointer to swtimer table.
|
||||
lua_pushstring(L, SUSP_LIST_STR);
|
||||
lua_pushlightuserdata(L, suspended_timer_list_head);
|
||||
lua_rawset(L, -3);
|
||||
|
||||
//pop swtimer table from stack
|
||||
lua_pop(L, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
void swtmr_resume_timers(){
|
||||
lua_State* L = lua_getstate();
|
||||
|
||||
//get swtimer table
|
||||
push_swtmr_registry_key(L);
|
||||
lua_rawget(L, L_REGISTRY);
|
||||
|
||||
//get suspended_timer_list lightuserdata
|
||||
lua_pushstring(L, SUSP_LIST_STR);
|
||||
lua_rawget(L, -2);
|
||||
|
||||
//check for existence of swtimer table and the suspended_timer_list pointer userdata, return if not found
|
||||
if(!lua_istable(L, -2) || !lua_isuserdata(L, -1)){
|
||||
// not necessarily an error maybe there are legitimately no timers to resume
|
||||
lua_pop(L, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
os_timer_t* suspended_timer_list_ptr = lua_touserdata(L, -1);
|
||||
lua_pop(L, 1); //pop suspended timer list userdata from stack
|
||||
|
||||
//since timers will be resumed, the suspended_timer_list lightuserdata can be cleared from swtimer table
|
||||
lua_pushstring(L, SUSP_LIST_STR);
|
||||
lua_pushnil(L);
|
||||
lua_rawset(L, -3);
|
||||
|
||||
|
||||
lua_pop(L, 1); //pop swtimer table from stack
|
||||
|
||||
volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS);
|
||||
|
||||
//this section does the actual resuming of the suspended timer(s)
|
||||
while(suspended_timer_list_ptr != NULL){
|
||||
os_timer_t* timer_list_ptr = timer_list;
|
||||
|
||||
//the pointer to next suspended timer must be saved, the current suspended timer will be removed from the list
|
||||
os_timer_t* next_suspended_timer_ptr = suspended_timer_list_ptr->timer_next;
|
||||
|
||||
suspended_timer_list_ptr->timer_expire += frc2_count;
|
||||
|
||||
//traverse timer_list to determine where to insert suspended timer
|
||||
while(timer_list_ptr != NULL){
|
||||
if(suspended_timer_list_ptr->timer_expire > timer_list_ptr->timer_expire){
|
||||
if(timer_list_ptr->timer_next != NULL){
|
||||
//current timer is not at tail of timer_list
|
||||
if(suspended_timer_list_ptr->timer_expire < timer_list_ptr->timer_next->timer_expire){
|
||||
//insert suspended timer between current timer and next timer
|
||||
suspended_timer_list_ptr->timer_next = timer_list_ptr->timer_next;
|
||||
timer_list_ptr->timer_next = suspended_timer_list_ptr;
|
||||
break; //timer resumed exit while loop
|
||||
}
|
||||
else{
|
||||
//suspended timer expire is larger than next timer
|
||||
}
|
||||
}
|
||||
else{
|
||||
//current timer is at tail of timer_list and suspended timer expire is greater then current timer
|
||||
//append timer to end of timer_list
|
||||
timer_list_ptr->timer_next = suspended_timer_list_ptr;
|
||||
suspended_timer_list_ptr->timer_next = NULL;
|
||||
break; //timer resumed exit while loop
|
||||
}
|
||||
}
|
||||
else if(timer_list_ptr == timer_list){
|
||||
//insert timer at head of list
|
||||
suspended_timer_list_ptr->timer_next = timer_list_ptr;
|
||||
timer_list = timer_list_ptr = suspended_timer_list_ptr;
|
||||
break; //timer resumed exit while loop
|
||||
}
|
||||
//suspended timer expire is larger than next timer
|
||||
//timer not resumed, next timer in timer_list
|
||||
timer_list_ptr = timer_list_ptr->timer_next;
|
||||
}
|
||||
//timer was resumed, next suspended timer
|
||||
suspended_timer_list_ptr = next_suspended_timer_ptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//this function registers a timer callback pointer in a lua table
|
||||
void swtmr_cb_register(void* timer_cb_ptr, uint8 suspend_policy){
|
||||
lua_State* L = lua_getstate();
|
||||
if(!L){
|
||||
//Lua has not started yet, therefore L_REGISTRY is not available.
|
||||
//add timer cb to queue for later processing after Lua has started
|
||||
add_to_reg_queue(timer_cb_ptr, suspend_policy);
|
||||
return;
|
||||
}
|
||||
if(timer_cb_ptr){
|
||||
size_t cb_list_last_idx = 0;
|
||||
|
||||
push_swtmr_registry_key(L);
|
||||
lua_rawget(L, L_REGISTRY);
|
||||
|
||||
if(!lua_istable(L, -1)){
|
||||
//swtmr does not exist, create and add to registry
|
||||
lua_pop(L, 1);
|
||||
lua_newtable(L);//push new table for swtmr.timer_cb_list
|
||||
// add swtimer table to L_REGISTRY
|
||||
push_swtmr_registry_key(L);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_rawset(L, L_REGISTRY);
|
||||
}
|
||||
|
||||
lua_pushstring(L, CB_LIST_STR);
|
||||
lua_rawget(L, -2);
|
||||
|
||||
if(lua_istable(L, -1)){
|
||||
//cb_list exists, get length of list
|
||||
cb_list_last_idx = lua_objlen(L, -1);
|
||||
}
|
||||
else{
|
||||
//cb_list does not exist in swtmr, create and add to swtmr
|
||||
lua_pop(L, 1);// pop nil value from stack
|
||||
lua_newtable(L);//create new table for swtmr.timer_cb_list
|
||||
lua_pushstring(L, CB_LIST_STR); //push name for the new table onto the stack
|
||||
lua_pushvalue(L, -2); //push table to top of stack
|
||||
lua_rawset(L, -4); //pop table and name from stack and register in swtmr
|
||||
}
|
||||
|
||||
//append new timer cb ptr to table
|
||||
lua_pushnumber(L, cb_list_last_idx+1);
|
||||
cb_registry_item_t* reg_item = lua_newuserdata(L, sizeof(cb_registry_item_t));
|
||||
reg_item->tmr_cb_ptr = timer_cb_ptr;
|
||||
reg_item->suspend_policy = suspend_policy;
|
||||
lua_rawset(L, -3);
|
||||
|
||||
//clear items pushed onto stack by this function
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//this function adds the timer cb ptr to a queue for later registration after lua has started
|
||||
static void add_to_reg_queue(void* timer_cb_ptr, uint8 suspend_policy){
|
||||
if(!timer_cb_ptr)
|
||||
return;
|
||||
tmr_cb_queue_t* queue_temp = c_zalloc(sizeof(tmr_cb_queue_t));
|
||||
if(!queue_temp){
|
||||
//it's boot time currently and we're already out of memory, something is very wrong...
|
||||
c_printf("\n\t%s:out of memory, system halted!\n", __FUNCTION__);
|
||||
while(1)
|
||||
system_soft_wdt_feed();
|
||||
}
|
||||
queue_temp->tmr_cb_ptr = timer_cb_ptr;
|
||||
queue_temp->suspend_policy = suspend_policy;
|
||||
queue_temp->next = NULL;
|
||||
|
||||
if(register_queue == NULL){
|
||||
register_queue = queue_temp;
|
||||
}
|
||||
else{
|
||||
tmr_cb_queue_t* queue_ptr = register_queue;
|
||||
while(queue_ptr->next != NULL){
|
||||
queue_ptr = queue_ptr->next;
|
||||
}
|
||||
queue_ptr->next = queue_temp;
|
||||
}
|
||||
if(!cb_register_task_id){
|
||||
cb_register_task_id = task_get_id(process_cb_register_queue);//get task id from task interface
|
||||
task_post_low(cb_register_task_id, false); //post task to process next item in queue
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void process_cb_register_queue(task_param_t param, uint8 priority)
|
||||
{
|
||||
if(!lua_getstate()){
|
||||
SWTMR_DBG("L== NULL, Lua not yet started! posting task");
|
||||
task_post_low(cb_register_task_id, false); //post task to process next item in queue
|
||||
return;
|
||||
}
|
||||
while(register_queue != NULL){
|
||||
tmr_cb_queue_t* register_queue_ptr = register_queue;
|
||||
void* cb_ptr_tmp = register_queue_ptr->tmr_cb_ptr;
|
||||
swtmr_cb_register(cb_ptr_tmp, register_queue_ptr->suspend_policy);
|
||||
register_queue = register_queue->next;
|
||||
c_free(register_queue_ptr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef SWTMR_DEBUG
|
||||
int print_timer_list(lua_State* L){
|
||||
push_swtmr_registry_key(L);
|
||||
lua_rawget(L, L_REGISTRY);
|
||||
lua_pushstring(L, CB_LIST_STR);
|
||||
lua_rawget(L, -2);
|
||||
if(!lua_istable(L, -2) || !lua_istable(L, -1)){
|
||||
lua_pop(L, 2);
|
||||
return 0;
|
||||
}
|
||||
os_timer_t* suspended_timer_list_head = NULL;
|
||||
os_timer_t* suspended_timer_list_tail = NULL;
|
||||
lua_pushstring(L, SUSP_LIST_STR);
|
||||
lua_rawget(L, -3);
|
||||
if(lua_isuserdata(L, -1)){
|
||||
suspended_timer_list_head = suspended_timer_list_tail = lua_touserdata(L, -1);
|
||||
while(suspended_timer_list_tail->timer_next != NULL){
|
||||
suspended_timer_list_tail = suspended_timer_list_tail->timer_next;
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
size_t registered_cb_qty = lua_objlen(L, -1);
|
||||
cb_registry_item_t** cb_reg_array = c_zalloc(sizeof(cb_registry_item_t*)*registered_cb_qty);
|
||||
if(!cb_reg_array){
|
||||
luaL_error(L, "%s: unable to suspend timers, out of memory!", __func__);
|
||||
return 0;
|
||||
}
|
||||
uint8 index = 0;
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L, -2) != 0){
|
||||
if(lua_isuserdata(L, -1)){
|
||||
cb_reg_array[index] = lua_touserdata(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
index++;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
|
||||
os_timer_t* timer_list_ptr = timer_list;
|
||||
c_printf("\n\tCurrent FRC2: %u\n", RTC_REG_READ(FRC2_COUNT_ADDRESS));
|
||||
c_printf("\ttimer_list:\n");
|
||||
while(timer_list_ptr != NULL){
|
||||
bool registered_flag = FALSE;
|
||||
for(int i=0; i < registered_cb_qty; i++){
|
||||
if(timer_list_ptr->timer_func == cb_reg_array[i]->tmr_cb_ptr){
|
||||
registered_flag = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
c_printf("\tptr:%p\tcb:%p\texpire:%8u\tperiod:%8u\tnext:%p\t%s\n",
|
||||
timer_list_ptr, timer_list_ptr->timer_func, timer_list_ptr->timer_expire, timer_list_ptr->timer_period, timer_list_ptr->timer_next, registered_flag ? "Registered" : "");
|
||||
timer_list_ptr = timer_list_ptr->timer_next;
|
||||
}
|
||||
|
||||
c_free(cb_reg_array);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int print_susp_timer_list(lua_State* L){
|
||||
push_swtmr_registry_key(L);
|
||||
lua_rawget(L, L_REGISTRY);
|
||||
|
||||
if(!lua_istable(L, -1)){
|
||||
return luaL_error(L, "swtmr table not found!");
|
||||
}
|
||||
|
||||
lua_pushstring(L, SUSP_LIST_STR);
|
||||
lua_rawget(L, -2);
|
||||
|
||||
if(!lua_isuserdata(L, -1)){
|
||||
return luaL_error(L, "swtmr.suspended_list userdata not found!");
|
||||
}
|
||||
|
||||
os_timer_t* susp_timer_list_ptr = lua_touserdata(L, -1);
|
||||
c_printf("\n\tsuspended_timer_list:\n");
|
||||
while(susp_timer_list_ptr != NULL){
|
||||
c_printf("\tptr:%p\tcb:%p\texpire:%8u\tperiod:%8u\tnext:%p\n",susp_timer_list_ptr, susp_timer_list_ptr->timer_func, susp_timer_list_ptr->timer_expire, susp_timer_list_ptr->timer_period, susp_timer_list_ptr->timer_next);
|
||||
susp_timer_list_ptr = susp_timer_list_ptr->timer_next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int suspend_timers_lua(lua_State* L){
|
||||
swtmr_suspend_timers();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int resume_timers_lua(lua_State* L){
|
||||
swtmr_resume_timers();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const LUA_REG_TYPE test_swtimer_debug_map[] = {
|
||||
{ LSTRKEY( "timer_list" ), LFUNCVAL( print_timer_list ) },
|
||||
{ LSTRKEY( "susp_timer_list" ), LFUNCVAL( print_susp_timer_list ) },
|
||||
{ LSTRKEY( "suspend" ), LFUNCVAL( suspend_timers_lua ) },
|
||||
{ LSTRKEY( "resume" ), LFUNCVAL( resume_timers_lua ) },
|
||||
{ LNILKEY, LNILVAL }
|
||||
};
|
||||
|
||||
NODEMCU_MODULE(SWTMR_DBG, "SWTMR_DBG", test_swtimer_debug_map, NULL);
|
||||
|
||||
#endif
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
#include "c_string.h"
|
||||
#include "user_interface.h"
|
||||
#include "smart.h"
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
#define ADDR_MAP_NUM 10
|
||||
|
||||
|
@ -500,6 +501,9 @@ void smart_end(){
|
|||
|
||||
os_timer_disarm(&smart_timer);
|
||||
os_timer_setfn(&smart_timer, (os_timer_func_t *)station_check_connect, (void *)1);
|
||||
SWTIMER_REG_CB(station_check_connect, SWTIMER_RESUME);
|
||||
//the function station_check_connect continues the Smart config process and fires the developers callback upon successful connection to the access point.
|
||||
//If this function manages to get suspended, I think it would be fine to resume the timer.
|
||||
os_timer_arm(&smart_timer, STATION_CHECK_TIME, 0); // no repeat
|
||||
}
|
||||
}
|
||||
|
@ -672,6 +676,9 @@ void smart_begin(int chnl, smart_succeed s, void *arg){
|
|||
wifi_set_promiscuous_rx_cb(detect);
|
||||
os_timer_disarm(&smart_timer);
|
||||
os_timer_setfn(&smart_timer, (os_timer_func_t *)smart_next_channel, NULL);
|
||||
SWTIMER_REG_CB(smart_next_channel, SWTIMER_RESUME);
|
||||
//smart_next_channel switches the wifi channel
|
||||
//I don't see a problem with resuming this timer
|
||||
os_timer_arm(&smart_timer, TIME_OUT_PER_CHANNEL, 0); // no repeat
|
||||
|
||||
if(s){
|
||||
|
@ -717,5 +724,6 @@ void station_check_connect(bool smart){
|
|||
}
|
||||
os_timer_disarm(&smart_timer);
|
||||
os_timer_setfn(&smart_timer, (os_timer_func_t *)station_check_connect, (void *)(int)smart);
|
||||
//this function was already registered in the function smart_end.
|
||||
os_timer_arm(&smart_timer, STATION_CHECK_TIME, 0); // no repeat
|
||||
}
|
||||
|
|
|
@ -576,8 +576,11 @@ static int esp8266_Sleep( sqlite3_vfs * vfs, int microseconds )
|
|||
|
||||
static int esp8266_CurrentTime( sqlite3_vfs * vfs, double * result )
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
*result = t / 86400.0 + 2440587.5;
|
||||
// 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;
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
|
||||
#############################################################
|
||||
# Required variables for each makefile
|
||||
# Discard this section from all parent makefiles
|
||||
# Expected variables (with automatic defaults):
|
||||
# CSRCS (all "C" files in the dir)
|
||||
# SUBDIRS (all subdirs with a Makefile)
|
||||
# GEN_LIBS - list of libs to be generated ()
|
||||
# GEN_IMAGES - list of images to be generated ()
|
||||
# COMPONENTS_xxx - a list of libs/objs in the form
|
||||
# subdir/lib to be extracted and rolled up into
|
||||
# a generated lib/image xxx.a ()
|
||||
#
|
||||
ifndef PDIR
|
||||
GEN_LIBS = libswtimer.a
|
||||
endif
|
||||
|
||||
STD_CFLAGS=-std=gnu11 -Wimplicit
|
||||
|
||||
#############################################################
|
||||
# Configuration i.e. compile options etc.
|
||||
# Target specific stuff (defines etc.) goes in here!
|
||||
# Generally values applying to a tree are captured in the
|
||||
# makefile at its root level - these are then overridden
|
||||
# for a subtree within the makefile rooted therein
|
||||
#
|
||||
#DEFINES +=
|
||||
|
||||
#############################################################
|
||||
# Recursion Magic - Don't touch this!!
|
||||
#
|
||||
# Each subtree potentially has an include directory
|
||||
# corresponding to the common APIs applicable to modules
|
||||
# rooted at that subtree. Accordingly, the INCLUDE PATH
|
||||
# of a module can only contain the include directories up
|
||||
# its parent path, and not its siblings
|
||||
#
|
||||
# Required for each makefile to inherit from the parent
|
||||
#
|
||||
|
||||
INCLUDES := $(INCLUDES) -I $(PDIR)include
|
||||
INCLUDES += -I ./
|
||||
INCLUDES += -I ./include
|
||||
INCLUDES += -I ../include
|
||||
INCLUDES += -I ../../include
|
||||
INCLUDES += -I ../lua
|
||||
INCLUDES += -I ../platform
|
||||
INCLUDES += -I ../libc
|
||||
|
||||
PDIR := ../$(PDIR)
|
||||
sinclude $(PDIR)Makefile
|
||||
|
|
@ -1,632 +0,0 @@
|
|||
/* swTimer.c SDK timer suspend API
|
||||
*
|
||||
* SDK software timer API info:
|
||||
*
|
||||
* The SDK software timer uses a linked list called `os_timer_t* timer_list` to keep track of
|
||||
* all currently armed timers.
|
||||
*
|
||||
* The SDK software timer API executes in a task. The priority of this task in relation to the
|
||||
* application level tasks is unknown (at time of writing).
|
||||
*
|
||||
*
|
||||
* To determine when a timer's callback should be executed, the respective timer's `timer_expire`
|
||||
* variable is compared to the hardware counter(FRC2), then, if the timer's `timer_expire` is
|
||||
* less than the current FRC2 count, the timer's callback is fired.
|
||||
*
|
||||
* The timers in this list are organized in an ascending order starting with the timer
|
||||
* with the lowest timer_expire.
|
||||
*
|
||||
* When a timer expires that has a timer_period greater than 0, timer_expire is changed to
|
||||
* current FRC2 + timer_period, then the timer is inserted back in to the list in the correct position.
|
||||
*
|
||||
* when using millisecond(default) timers, FRC2 resolution is 312.5 ticks per millisecond.
|
||||
*
|
||||
*
|
||||
* TIMER SUSPEND API:
|
||||
*
|
||||
* Timer registry:
|
||||
* void sw_timer_register(void* timer_ptr);
|
||||
* - Adds timers to the timer registry by adding it to a queue that is later
|
||||
* processed by timer_register_task that performs the registry maintenance
|
||||
*
|
||||
* void sw_timer_unregister(void* timer_ptr);
|
||||
* - Removes timers from the timer registry by adding it to a queue that is later
|
||||
* processed by timer_unregister_task that performs the registry maintenance
|
||||
*
|
||||
*
|
||||
* int sw_timer_suspend(os_timer_t* timer_ptr);
|
||||
* - Suspend a single active timer or suspend all active timers.
|
||||
* - if no timer pointer is provided, timer_ptr == NULL, then all currently active timers will be suspended.
|
||||
*
|
||||
* int sw_timer_resume(os_timer_t* timer_ptr);
|
||||
* - Resume a single suspended timer or resume all suspended timers.
|
||||
* - if no timer pointer is provided, timer_ptr == NULL, then all currently suspended timers will be resumed.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include "swTimer/swTimer.h"
|
||||
#include "c_stdio.h"
|
||||
#include "misc/dynarr.h"
|
||||
#include "task/task.h"
|
||||
|
||||
#ifdef ENABLE_TIMER_SUSPEND
|
||||
|
||||
/* Settings */
|
||||
#define TIMER_REGISTRY_INITIAL_SIZE 10
|
||||
#ifdef USE_SWTMR_ERROR_STRINGS
|
||||
static const char* SWTMR_ERROR_STRINGS[]={
|
||||
[SWTMR_MALLOC_FAIL] = "Out of memory!",
|
||||
[SWTMR_TIMER_NOT_ARMED] = "Timer is not armed",
|
||||
// [SWTMR_NULL_PTR] = "A NULL pointer was passed to timer suspend api",
|
||||
[SWTMR_REGISTRY_NO_REGISTERED_TIMERS] = "No timers in registry",
|
||||
// [SWTMR_SUSPEND_ARRAY_INITIALIZATION_FAILED] = "Suspend array init fail",
|
||||
// [SWTMR_SUSPEND_ARRAY_ADD_FAILED] = "Unable to add suspended timer to array",
|
||||
// [SWTMR_SUSPEND_ARRAY_REMOVE_FAILED] = "Unable to remove suspended timer from array",
|
||||
[SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED] = "Already suspended",
|
||||
[SWTMR_SUSPEND_TIMER_ALREADY_REARMED] = "Already been re-armed",
|
||||
[SWTMR_SUSPEND_NO_SUSPENDED_TIMERS] = "No suspended timers",
|
||||
[SWTMR_SUSPEND_TIMER_NOT_SUSPENDED] = "Not suspended",
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Private Function Declarations */
|
||||
static inline bool timer_armed_check(os_timer_t* timer_ptr);
|
||||
static inline int timer_do_suspend(os_timer_t* timer_ptr);
|
||||
static inline int timer_do_resume_single(os_timer_t** suspended_timer_ptr);
|
||||
static void timer_register_task(task_param_t param, uint8 priority);
|
||||
static inline os_timer_t** timer_registry_check(os_timer_t* timer_ptr);
|
||||
static inline void timer_registry_remove_unarmed(void);
|
||||
static inline os_timer_t** timer_suspended_check(os_timer_t* timer_ptr);
|
||||
static void timer_unregister_task(task_param_t param, uint8 priority);
|
||||
|
||||
/* Private Variable Definitions */
|
||||
static task_handle_t timer_reg_task_id = false;
|
||||
static task_handle_t timer_unreg_task_id = false;
|
||||
|
||||
static dynarr_t timer_registry = {0};
|
||||
static dynarr_t suspended_timers = {0};
|
||||
|
||||
typedef struct registry_queue{
|
||||
struct registry_queue* next;
|
||||
os_timer_t* timer_ptr;
|
||||
}registry_queue_t;
|
||||
|
||||
static registry_queue_t* register_queue = NULL;
|
||||
static registry_queue_t* unregister_queue = NULL;
|
||||
|
||||
/* Private Function Definitions */
|
||||
|
||||
//NOTE: Interrupts are temporarily blocked during the execution of this function
|
||||
static inline bool timer_armed_check(os_timer_t* timer_ptr){
|
||||
bool retval = FALSE;
|
||||
|
||||
// we are messing around with the SDK timer structure here, may not be necessary, better safe than sorry though.
|
||||
ETS_INTR_LOCK();
|
||||
|
||||
os_timer_t* timer_list_ptr = timer_list; //get head node pointer of timer_list
|
||||
|
||||
//if present find timer_ptr in timer_list rand return result
|
||||
while(timer_list_ptr != NULL){
|
||||
if(timer_list_ptr == timer_ptr){
|
||||
retval = TRUE;
|
||||
break;
|
||||
}
|
||||
timer_list_ptr = timer_list_ptr->timer_next;
|
||||
}
|
||||
|
||||
//we are done with timer_list, it is now safe to unlock interrupts
|
||||
ETS_INTR_UNLOCK();
|
||||
|
||||
//return value
|
||||
return retval;
|
||||
}
|
||||
|
||||
static inline int timer_do_suspend(os_timer_t* timer_ptr){
|
||||
if(timer_ptr == NULL){
|
||||
SWTMR_ERR("timer_ptr is invalid");
|
||||
return SWTMR_FAIL;
|
||||
}
|
||||
|
||||
volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS);
|
||||
|
||||
if(timer_armed_check(timer_ptr) == FALSE){
|
||||
return SWTMR_TIMER_NOT_ARMED;
|
||||
}
|
||||
|
||||
os_timer_t** suspended_timer_ptr = timer_suspended_check(timer_ptr);
|
||||
|
||||
uint32 expire_temp = 0;
|
||||
uint32 period_temp = timer_ptr->timer_period;
|
||||
|
||||
if(timer_ptr->timer_expire < frc2_count){
|
||||
expire_temp = 5; // 16 us in ticks (1 tick = ~3.2 us) (arbitrarily chosen value)
|
||||
}
|
||||
else{
|
||||
expire_temp = timer_ptr->timer_expire - frc2_count;
|
||||
}
|
||||
|
||||
ets_timer_disarm(timer_ptr);
|
||||
timer_unregister_task((task_param_t)timer_ptr, false);
|
||||
|
||||
timer_ptr->timer_expire = expire_temp;
|
||||
timer_ptr->timer_period = period_temp;
|
||||
|
||||
if(suspended_timers.data_ptr == NULL){
|
||||
if(!dynarr_init(&suspended_timers, 10, sizeof(os_timer_t*))){
|
||||
SWTMR_ERR("Suspend array init fail");
|
||||
return SWTMR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if(suspended_timer_ptr == NULL){
|
||||
// return SWTMR_SUSPEND_TIMER_ALREADY_SUSPENDED;
|
||||
if(!dynarr_add(&suspended_timers, &timer_ptr, sizeof(timer_ptr))){
|
||||
SWTMR_ERR("Unable to add suspended timer to array");
|
||||
return SWTMR_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return SWTMR_OK;
|
||||
}
|
||||
|
||||
//NOTE: Interrupts are temporarily blocked during the execution of this function
|
||||
static inline int timer_do_resume_single(os_timer_t** suspended_timer_ptr){
|
||||
if(suspended_timer_ptr == NULL){
|
||||
SWTMR_ERR("suspended_timer_ptr is invalid");
|
||||
return SWTMR_FAIL;
|
||||
}
|
||||
|
||||
os_timer_t* timer_list_ptr = NULL;
|
||||
os_timer_t* resume_timer_ptr = *suspended_timer_ptr;
|
||||
volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS);
|
||||
|
||||
//verify timer has not been rearmed
|
||||
if(timer_armed_check(resume_timer_ptr) == TRUE){
|
||||
SWTMR_DBG("Timer(%p) already rearmed, removing from array", resume_timer_ptr);
|
||||
if(!dynarr_remove(&suspended_timers, suspended_timer_ptr)){
|
||||
SWTMR_ERR("Failed to remove timer from suspend array");
|
||||
return SWTMR_FAIL;
|
||||
}
|
||||
return SWTMR_OK;
|
||||
}
|
||||
|
||||
//Prepare timer for resume
|
||||
resume_timer_ptr->timer_expire += frc2_count;
|
||||
|
||||
timer_register_task((task_param_t)resume_timer_ptr, false);
|
||||
SWTMR_DBG("Removing timer(%p) from suspend array", resume_timer_ptr);
|
||||
|
||||
//This section performs the actual resume of the suspended timer
|
||||
|
||||
// we are messing around with the SDK timer structure here, may not be necessary, better safe than sorry though.
|
||||
ETS_INTR_LOCK();
|
||||
|
||||
timer_list_ptr = timer_list;
|
||||
|
||||
while(timer_list_ptr != NULL){
|
||||
if(resume_timer_ptr->timer_expire > timer_list_ptr->timer_expire){
|
||||
if(timer_list_ptr->timer_next != NULL){
|
||||
if(resume_timer_ptr->timer_expire < timer_list_ptr->timer_next->timer_expire){
|
||||
resume_timer_ptr->timer_next = timer_list_ptr->timer_next;
|
||||
timer_list_ptr->timer_next = resume_timer_ptr;
|
||||
break;
|
||||
}
|
||||
else{
|
||||
//next timer in timer_list
|
||||
}
|
||||
}
|
||||
else{
|
||||
timer_list_ptr->timer_next = resume_timer_ptr;
|
||||
resume_timer_ptr->timer_next = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(timer_list_ptr == timer_list){
|
||||
resume_timer_ptr->timer_next=timer_list_ptr;
|
||||
timer_list = timer_list_ptr = resume_timer_ptr;
|
||||
break;
|
||||
}
|
||||
|
||||
timer_list_ptr = timer_list_ptr->timer_next;
|
||||
}
|
||||
|
||||
//we no longer need to block interrupts
|
||||
ETS_INTR_UNLOCK();
|
||||
|
||||
return SWTMR_OK;
|
||||
}
|
||||
|
||||
static void timer_register_task(task_param_t param, uint8 priority){
|
||||
if(timer_registry.data_ptr==NULL){
|
||||
if(!dynarr_init(&timer_registry, TIMER_REGISTRY_INITIAL_SIZE, sizeof(os_timer_t*))){
|
||||
SWTMR_ERR("timer registry init Fail!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
os_timer_t* timer_ptr = NULL;
|
||||
|
||||
//if a timer pointer is provided, override normal queue processing behavior
|
||||
if(param != 0){
|
||||
timer_ptr = (os_timer_t*)param;
|
||||
}
|
||||
else{
|
||||
//process an item in the register queue
|
||||
if(register_queue == NULL){
|
||||
/**/SWTMR_ERR("ERROR: REGISTER QUEUE EMPTY");
|
||||
return;
|
||||
}
|
||||
|
||||
registry_queue_t* queue_temp = register_queue;
|
||||
register_queue = register_queue->next;
|
||||
|
||||
timer_ptr = queue_temp->timer_ptr;
|
||||
|
||||
c_free(queue_temp);
|
||||
|
||||
if(register_queue != NULL){
|
||||
SWTMR_DBG("register_queue not empty, posting task");
|
||||
task_post_low(timer_reg_task_id, false);
|
||||
}
|
||||
}
|
||||
|
||||
os_timer_t** suspended_tmr_ptr = timer_suspended_check(timer_ptr);
|
||||
if(suspended_tmr_ptr != NULL){
|
||||
if(!dynarr_remove(&suspended_timers, suspended_tmr_ptr)){
|
||||
SWTMR_ERR("failed to remove %p from suspend registry", suspended_tmr_ptr);
|
||||
}
|
||||
SWTMR_DBG("removed timer from suspended timers");
|
||||
}
|
||||
|
||||
if(timer_registry_check(timer_ptr) != NULL){
|
||||
/**/SWTMR_DBG("timer(%p) found in registry, returning", timer_ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!dynarr_add(&timer_registry, &timer_ptr, sizeof(timer_ptr))){
|
||||
/**/SWTMR_ERR("Registry append failed");
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static inline os_timer_t** timer_registry_check(os_timer_t* timer_ptr){
|
||||
if(timer_registry.data_ptr == NULL){
|
||||
return NULL;
|
||||
}
|
||||
if(timer_registry.used > 0){
|
||||
|
||||
os_timer_t** timer_registry_array = timer_registry.data_ptr;
|
||||
|
||||
for(uint32 i=0; i < timer_registry.used; i++){
|
||||
if(timer_registry_array[i] == timer_ptr){
|
||||
/**/SWTMR_DBG("timer(%p) is registered", timer_registry_array[i]);
|
||||
return &timer_registry_array[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void timer_registry_remove_unarmed(void){
|
||||
if(timer_registry.data_ptr == NULL){
|
||||
return;
|
||||
}
|
||||
if(timer_registry.used > 0){
|
||||
os_timer_t** timer_registry_array = timer_registry.data_ptr;
|
||||
for(uint32 i=0; i < timer_registry.used; i++){
|
||||
if(timer_armed_check(timer_registry_array[i]) == FALSE){
|
||||
timer_unregister_task((task_param_t)timer_registry_array[i], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline os_timer_t** timer_suspended_check(os_timer_t* timer_ptr){
|
||||
if(suspended_timers.data_ptr == NULL){
|
||||
return NULL;
|
||||
}
|
||||
if(suspended_timers.used > 0){
|
||||
|
||||
os_timer_t** suspended_timer_array = suspended_timers.data_ptr;
|
||||
|
||||
for(uint32 i=0; i < suspended_timers.used; i++){
|
||||
if(suspended_timer_array[i] == timer_ptr){
|
||||
return &suspended_timer_array[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void timer_unregister_task(task_param_t param, uint8 priority){
|
||||
if(timer_registry.data_ptr == NULL){
|
||||
return;
|
||||
}
|
||||
os_timer_t* timer_ptr = NULL;
|
||||
|
||||
if(param != false){
|
||||
timer_ptr = (os_timer_t*)param;
|
||||
}
|
||||
else{
|
||||
|
||||
if(unregister_queue == NULL) {
|
||||
SWTMR_ERR("ERROR register queue empty");
|
||||
return;
|
||||
}
|
||||
registry_queue_t* queue_temp = unregister_queue;
|
||||
timer_ptr = queue_temp->timer_ptr;
|
||||
unregister_queue = unregister_queue->next;
|
||||
c_free(queue_temp);
|
||||
if(unregister_queue != NULL){
|
||||
SWTMR_DBG("unregister_queue not empty, posting task");
|
||||
task_post_low(timer_unreg_task_id, false);
|
||||
}
|
||||
}
|
||||
if(timer_armed_check(timer_ptr) == TRUE){
|
||||
SWTMR_DBG("%p still armed, can't remove from registry", timer_ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
os_timer_t** registry_ptr = timer_registry_check(timer_ptr);
|
||||
if(registry_ptr != NULL){
|
||||
if(!dynarr_remove(&timer_registry, registry_ptr)){
|
||||
|
||||
/**/SWTMR_ERR("Failed to remove timer from registry");
|
||||
/**/SWTMR_DBG("registry_ptr = %p", registry_ptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else{
|
||||
//timer not in registry
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Global Function Definitions */
|
||||
|
||||
#if defined(SWTMR_DEBUG)
|
||||
|
||||
void swtmr_print_registry(void){
|
||||
volatile uint32 frc2_count = RTC_REG_READ(FRC2_COUNT_ADDRESS);
|
||||
uint32 time_till_fire = 0;
|
||||
uint32 time = system_get_time();
|
||||
|
||||
timer_registry_remove_unarmed();
|
||||
time = system_get_time()-time;
|
||||
|
||||
/**/SWTMR_DBG("registry_remove_unarmed_timers() took %u us", time);
|
||||
|
||||
os_timer_t** timer_array = timer_registry.data_ptr;
|
||||
|
||||
c_printf("\n array used(%u)/size(%u)\ttotal size(bytes)=%u\n FRC2 COUNT %u\n",
|
||||
timer_registry.used, timer_registry.array_size, timer_registry.array_size * timer_registry.data_size, frc2_count);
|
||||
c_printf("\n Registered timer array contents:\n");
|
||||
c_printf(" %-5s %-10s %-10s %-13s %-10s %-10s %-10s\n", "idx", "ptr", "expire", "period(tick)", "period(ms)", "fire(tick)", "fire(ms)");
|
||||
|
||||
for(uint32 i=0; i < timer_registry.used; i++){
|
||||
time_till_fire = (timer_array[i]->timer_expire - frc2_count);
|
||||
c_printf(" %-5d %-10p %-10d %-13d %-10d %-10d %-10d\n", i, timer_array[i], timer_array[i]->timer_expire, timer_array[i]->timer_period, (uint32)(timer_array[i]->timer_period/312.5), time_till_fire, (uint32)(time_till_fire/312.5));
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void swtmr_print_suspended(void){
|
||||
os_timer_t** susp_timer_array = suspended_timers.data_ptr;
|
||||
|
||||
c_printf("\n array used(%u)/size(%u)\ttotal size(bytes)=%u\n",
|
||||
suspended_timers.used, suspended_timers.array_size, suspended_timers.array_size * suspended_timers.data_size);
|
||||
c_printf("\n Suspended timer array contents:\n");
|
||||
c_printf(" %-5s %-10s %-15s %-15s %-14s %-10s\n", "idx", "ptr", "time left(tick)", "time left(ms)", "period(tick)", "period(ms)");
|
||||
|
||||
for(uint32 i=0; i < suspended_timers.used; i++){
|
||||
c_printf(" %-5d %-10p %-15d %-15d %-14d %-10d\n", i, susp_timer_array[i], susp_timer_array[i]->timer_expire, (uint32)(susp_timer_array[i]->timer_expire/312.5), susp_timer_array[i]->timer_period, (uint32)(susp_timer_array[i]->timer_period/312.5));
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void swtmr_print_timer_list(void){
|
||||
volatile uint32 frc2_count=RTC_REG_READ(FRC2_COUNT_ADDRESS);
|
||||
os_timer_t* timer_list_ptr=NULL;
|
||||
uint32 time_till_fire=0;
|
||||
c_printf("\n\tcurrent FRC2 count:%u\n", frc2_count);
|
||||
c_printf(" timer_list contents:\n");
|
||||
c_printf(" %-10s %-10s %-10s %-10s %-10s %-10s %-10s\n", "ptr", "expire", "period", "func", "arg", "fire(tick)", "fire(ms)");
|
||||
|
||||
ETS_INTR_LOCK();
|
||||
|
||||
timer_list_ptr=timer_list;
|
||||
|
||||
while(timer_list_ptr != NULL){
|
||||
time_till_fire=(timer_list_ptr->timer_expire - frc2_count) / 312.5;
|
||||
|
||||
c_printf(" %-10p %-10u %-10u %-10p %-10p %-10u %-10u\n",
|
||||
timer_list_ptr, (uint32)(timer_list_ptr->timer_expire),
|
||||
(uint32)(timer_list_ptr->timer_period ), timer_list_ptr->timer_func,
|
||||
timer_list_ptr->timer_arg, (timer_list_ptr->timer_expire - frc2_count), time_till_fire);
|
||||
|
||||
timer_list_ptr=timer_list_ptr->timer_next;
|
||||
}
|
||||
ETS_INTR_UNLOCK();
|
||||
c_printf(" NOTE: some timers in the above list belong to the SDK and can not be suspended\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int swtmr_suspend(os_timer_t* timer_ptr){
|
||||
int return_value = SWTMR_OK;
|
||||
|
||||
if(timer_ptr != NULL){
|
||||
// Timer pointer was provided, suspending specified timer
|
||||
|
||||
return_value = timer_do_suspend(timer_ptr);
|
||||
|
||||
if(return_value != SWTMR_OK){
|
||||
return return_value;
|
||||
}
|
||||
}
|
||||
else{
|
||||
//timer pointer not found, suspending all timers
|
||||
|
||||
if(timer_registry.data_ptr == NULL){
|
||||
return SWTMR_REGISTRY_NO_REGISTERED_TIMERS;
|
||||
}
|
||||
|
||||
timer_registry_remove_unarmed();
|
||||
|
||||
os_timer_t** tmr_reg_arr = timer_registry.data_ptr;
|
||||
os_timer_t* temp_ptr = tmr_reg_arr[0];
|
||||
|
||||
while(temp_ptr != NULL){
|
||||
return_value = timer_do_suspend(temp_ptr);
|
||||
|
||||
if(return_value != SWTMR_OK){
|
||||
return return_value;
|
||||
}
|
||||
|
||||
temp_ptr = tmr_reg_arr[0];
|
||||
}
|
||||
}
|
||||
return return_value;
|
||||
}
|
||||
|
||||
int swtmr_resume(os_timer_t* timer_ptr){
|
||||
|
||||
if(suspended_timers.data_ptr == NULL){
|
||||
return SWTMR_SUSPEND_NO_SUSPENDED_TIMERS;
|
||||
}
|
||||
|
||||
os_timer_t** suspended_tmr_array = suspended_timers.data_ptr;
|
||||
os_timer_t** suspended_timer_ptr = NULL;
|
||||
int retval=SWTMR_OK;
|
||||
|
||||
if(timer_ptr != NULL){
|
||||
suspended_timer_ptr = timer_suspended_check(timer_ptr);
|
||||
if(suspended_timer_ptr == NULL){
|
||||
//timer not suspended
|
||||
return SWTMR_SUSPEND_TIMER_NOT_SUSPENDED;
|
||||
}
|
||||
|
||||
retval = timer_do_resume_single(suspended_timer_ptr);
|
||||
if(retval != SWTMR_OK){
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
else{
|
||||
suspended_timer_ptr = &suspended_tmr_array[0];
|
||||
|
||||
while(suspended_timers.used > 0){
|
||||
retval = timer_do_resume_single(suspended_timer_ptr);
|
||||
if(retval != SWTMR_OK){
|
||||
SWTMR_ERR("Unable to continue resuming timers, error(%u)", retval);
|
||||
return retval;
|
||||
}
|
||||
suspended_timer_ptr = &suspended_tmr_array[0];
|
||||
}
|
||||
}
|
||||
return SWTMR_OK;
|
||||
}
|
||||
|
||||
void swtmr_register(void* timer_ptr){
|
||||
if(timer_ptr == NULL){
|
||||
SWTMR_DBG("error: timer_ptr is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
registry_queue_t* queue_temp = c_zalloc(sizeof(registry_queue_t));
|
||||
|
||||
if(queue_temp == NULL){
|
||||
SWTMR_ERR("MALLOC FAIL! req:%u, free:%u", sizeof(registry_queue_t), system_get_free_heap_size());
|
||||
return;
|
||||
}
|
||||
queue_temp->timer_ptr = timer_ptr;
|
||||
|
||||
if(register_queue == NULL){
|
||||
register_queue = queue_temp;
|
||||
|
||||
if(timer_reg_task_id == false) timer_reg_task_id = task_get_id(timer_register_task);
|
||||
task_post_low(timer_reg_task_id, false);
|
||||
SWTMR_DBG("queue empty, adding timer(%p) to queue and posting task", timer_ptr);
|
||||
}
|
||||
else{
|
||||
registry_queue_t* register_queue_tail = register_queue;
|
||||
|
||||
while(register_queue_tail->next != NULL){
|
||||
register_queue_tail = register_queue_tail->next;
|
||||
}
|
||||
|
||||
register_queue_tail->next = queue_temp;
|
||||
SWTMR_DBG("queue NOT empty, appending timer(%p) to queue", timer_ptr);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void swtmr_unregister(void* timer_ptr){
|
||||
if(timer_ptr == NULL){
|
||||
SWTMR_DBG("error: timer_ptr is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
registry_queue_t* queue_temp = c_zalloc(sizeof(registry_queue_t));
|
||||
|
||||
if(queue_temp == NULL){
|
||||
SWTMR_ERR("MALLOC FAIL! req:%u, free:%u", sizeof(registry_queue_t), system_get_free_heap_size());
|
||||
return;
|
||||
}
|
||||
queue_temp->timer_ptr=timer_ptr;
|
||||
|
||||
if(unregister_queue == NULL){
|
||||
unregister_queue = queue_temp;
|
||||
if(timer_unreg_task_id==false) timer_unreg_task_id=task_get_id(timer_unregister_task);
|
||||
task_post_low(timer_unreg_task_id, false);
|
||||
SWTMR_DBG("queue empty, adding timer(%p) to queue and posting task", timer_ptr);
|
||||
}
|
||||
else{
|
||||
registry_queue_t* unregister_queue_tail=unregister_queue;
|
||||
while(unregister_queue_tail->next != NULL){
|
||||
unregister_queue_tail=unregister_queue_tail->next;
|
||||
}
|
||||
unregister_queue_tail->next = queue_temp;
|
||||
// SWTMR_DBG("queue NOT empty, appending timer(%p) to queue", timer_ptr);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const char* swtmr_errorcode2str(int error_value){
|
||||
#ifdef USE_SWTMR_ERROR_STRINGS
|
||||
if(SWTMR_ERROR_STRINGS[error_value] == NULL){
|
||||
SWTMR_ERR("error string %d not found", error_value);
|
||||
return NULL;
|
||||
}
|
||||
else{
|
||||
return SWTMR_ERROR_STRINGS[error_value];
|
||||
}
|
||||
#else
|
||||
SWTMR_ERR("error(%u)", error_value);
|
||||
return "ERROR! for more info, use debug build";
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool swtmr_suspended_test(os_timer_t* timer_ptr){
|
||||
os_timer_t** test_var = timer_suspended_check(timer_ptr);
|
||||
if(test_var == NULL){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -76,12 +76,6 @@ static tsl2561Gain_t _tsl2561Gain = TSL2561_GAIN_1X;
|
|||
static tsl2561Address_t tsl2561Address = TSL2561_ADDRESS_FLOAT;
|
||||
static tsl2561Package_t tsl2561Package = TSL2561_PACKAGE_T_FN_CL;
|
||||
|
||||
static void delay_ms(uint16_t ms)
|
||||
{
|
||||
while (ms--)
|
||||
os_delay_us(1000);
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@brief Writes an 8 bit values over I2C
|
||||
|
@ -236,13 +230,13 @@ tsl2561Error_t tsl2561GetLuminosity(uint16_t *broadband, uint16_t *ir) {
|
|||
// Wait x ms for ADC to complete
|
||||
switch (_tsl2561IntegrationTime) {
|
||||
case TSL2561_INTEGRATIONTIME_13MS:
|
||||
delay_ms(14); //systickDelay(14);
|
||||
os_delay_us(14000); //systickDelay(14);
|
||||
break;
|
||||
case TSL2561_INTEGRATIONTIME_101MS:
|
||||
delay_ms(102); //systickDelay(102);
|
||||
os_delay_us(102000); //systickDelay(102);
|
||||
break;
|
||||
default:
|
||||
delay_ms(404); //systickDelay(404);
|
||||
os_delay_us(404000); //systickDelay(404);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ STD_CFLAGS=-std=gnu11 -Wimplicit
|
|||
# makefile at its root level - these are then overridden
|
||||
# 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!!
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
#include "../crypto/digests.h"
|
||||
#include "../crypto/mech.h"
|
||||
|
||||
#include "pm/swtimer.h"
|
||||
|
||||
#define PROTOCOL_SECURE "wss://"
|
||||
#define PROTOCOL_INSECURE "ws://"
|
||||
|
||||
|
@ -560,6 +562,7 @@ static void ws_initReceiveCallback(void *arg, char *buf, unsigned short len) {
|
|||
|
||||
os_timer_disarm(&ws->timeoutTimer);
|
||||
os_timer_setfn(&ws->timeoutTimer, (os_timer_func_t *) ws_sendPingTimeout, conn);
|
||||
SWTIMER_REG_CB(ws_sendPingTimeout, SWTIMER_RESUME)
|
||||
os_timer_arm(&ws->timeoutTimer, WS_PING_INTERVAL_MS, true);
|
||||
|
||||
espconn_regist_recvcb(conn, ws_receiveCallback);
|
||||
|
@ -706,6 +709,7 @@ static void dns_callback(const char *hostname, ip_addr_t *addr, void *arg) {
|
|||
// Set connection timeout timer
|
||||
os_timer_disarm(&ws->timeoutTimer);
|
||||
os_timer_setfn(&ws->timeoutTimer, (os_timer_func_t *) ws_connectTimeout, conn);
|
||||
SWTIMER_REG_CB(ws_connectTimeout, SWTIMER_RESUME)
|
||||
os_timer_arm(&ws->timeoutTimer, WS_CONNECT_TIMEOUT_MS, false);
|
||||
|
||||
if (ws->isSecure) {
|
||||
|
@ -867,6 +871,7 @@ void ws_close(ws_info *ws) {
|
|||
|
||||
os_timer_disarm(&ws->timeoutTimer);
|
||||
os_timer_setfn(&ws->timeoutTimer, (os_timer_func_t *) ws_forceCloseTimeout, ws->conn);
|
||||
SWTIMER_REG_CB(ws_forceCloseTimeout, SWTIMER_RESUME);
|
||||
os_timer_arm(&ws->timeoutTimer, WS_FORCE_CLOSE_TIMEOUT_MS, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
!.gitignore
|
||||
!blank.bin
|
||||
!esp_init_data_default.bin
|
||||
!esp_init_data_default_v05.bin
|
||||
|
|
|
@ -75,7 +75,7 @@ make EXTRA_CCFLAGS="-DLUA_NUMBER_INTEGRAL ....
|
|||
Identify your firmware builds by editing `app/include/user_version.h`
|
||||
|
||||
```c
|
||||
#define NODE_VERSION "NodeMCU 2.1.0+myname"
|
||||
#define NODE_VERSION "NodeMCU " ESP_SDK_VERSION_STRING "." NODE_VERSION_XSTR(NODE_VERSION_INTERNAL)
|
||||
#ifndef BUILD_DATE
|
||||
#define BUILD_DATE "YYYYMMDD"
|
||||
#endif
|
||||
|
|
|
@ -102,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.
|
||||
|
||||
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.
|
||||
- `0xfc000` for 1 MB, modules like ESP8285, PSF-A85, some ESP-01, -03 etc.
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
# FAQ
|
||||
|
||||
*This FAQ was started by [Terry Ellison](https://github.com/TerryE) as an unofficial FAQ in mid 2015. This version as at April 2017 includes some significant rewrites.*
|
||||
*This FAQ was started by [Terry Ellison](https://github.com/TerryE) as an unofficial FAQ in mid 2015. This version as at April 2017 includes some significant rewrites.*
|
||||
|
||||
## What is this FAQ for?
|
||||
|
||||
This FAQ does not aim to help you to learn to program or even how to program in Lua. There are plenty of resources on the Internet for this, some of which are listed in [Where to start](#where-to-start). What this FAQ does is to answer some of the common questions that a competent Lua developer would ask in learning how to develop Lua applications for the ESP8266 based boards running the [NodeMcu firmware](https://github.com/nodemcu/nodemcu-firmware). This includes the NodeMCU Devkits. However, the scope of the firmware is far wider than this as it can be used on any ESP8266 module.
|
||||
This FAQ does not aim to help you to learn to program or even how to program in Lua. There are plenty of resources on the Internet for this, some of which are listed in [Where to start](#where-to-start). What this FAQ does is to answer some of the common questions that a competent Lua developer would ask in learning how to develop Lua applications for the ESP8266 based boards running the [NodeMcu firmware](https://github.com/nodemcu/nodemcu-firmware). This includes the NodeMCU Devkits. However, the scope of the firmware is far wider than this as it can be used on any ESP8266 module.
|
||||
|
||||
## What has changed since the first version of this FAQ?
|
||||
|
||||
The [NodeMCU company](http://NodeMCU.com/index_en.html) was set up by [Zeroday](zeroday@nodemcu.com) to develop and to market a set of Lua firmware-based development boards which employ the Espressif ESP8266 SoC. The initial development of the firmware was done by Zeroday and a colleague, Vowstar, in-house with the firmware being first open-sourced on Github in late 2014. In mid-2015, Zeroday decided to open the firmware development to a wider group of community developers, so the core group of developers now comprises 6 community developers (including this author), and we are also supported by another dozen or so active contributors, and two NodeMCU originators.
|
||||
The [NodeMCU company](http://NodeMCU.com/index_en.html) was set up by [Zeroday](zeroday@nodemcu.com) to develop and to market a set of Lua firmware-based development boards which employ the Espressif ESP8266 SoC. The initial development of the firmware was done by Zeroday and a colleague, Vowstar, in-house with the firmware being first open-sourced on Github in late 2014. In mid-2015, Zeroday decided to open the firmware development to a wider group of community developers, so the core group of developers now comprises 6 community developers (including this author), and we are also supported by another dozen or so active contributors, and two NodeMCU originators.
|
||||
|
||||
This larger active team has allowed us to address most of the outstanding issues present at the first version of this FAQ. These include:
|
||||
This larger active team has allowed us to address most of the outstanding issues present at the first version of this FAQ. These include:
|
||||
|
||||
- For some time the project was locked into an old SDK version, but we now regularly rebaseline to the current SDK version.
|
||||
- Johny Mattsson's software exception handler and my LCD patch have allowed us to move the bulk of constant data out of RAM and into the firmware address space, and as a result current builds now typically boot with over 40Kb free RAM instead of 15Kb free and the code density is roughly 40% better.
|
||||
|
@ -22,7 +22,7 @@ This larger active team has allowed us to address most of the outstanding issues
|
|||
- Johny Mattsson is currently leading an ESP32 port.
|
||||
- We have a lot more hardware modules supported.
|
||||
|
||||
Because the development is active this list will no doubt continue to be revised and updated. See the [development README](https://github.com/nodemcu/nodemcu-firmware/blob/dev/README.md) for more details.
|
||||
Because the development is active this list will no doubt continue to be revised and updated. See the [development README](https://github.com/nodemcu/nodemcu-firmware/blob/dev/README.md) for more details.
|
||||
|
||||
## Lua Language
|
||||
|
||||
|
@ -30,89 +30,103 @@ 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 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.
|
||||
* 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 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 **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.
|
||||
- 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.
|
||||
- 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 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).
|
||||
|
||||
### How is NodeMCU Lua different to standard Lua?
|
||||
|
||||
Whilst the Lua standard distribution includes a stand-alone Lua interpreter, Lua itself is primarily an *extension language* that makes no assumptions about a "main" program: Lua works embedded in a host application to provide a powerful, lightweight scripting language for use within the application. This host application can then invoke functions to execute a piece of Lua code, can write and read Lua variables, and can register C functions to be called by Lua code. Through the use of C functions, Lua can be augmented to cope with a wide range of different domains, thus creating customized programming languages sharing a syntactical framework.
|
||||
|
||||
The ESP8266 was designed and is fabricated in China by [Espressif Systems](http://espressif.com/new-sdk-release/). Espressif have also developed and released a companion software development kit (SDK) to enable developers to build practical IoT applications for the ESP8266. The SDK is made freely available to developers in the form of binary libraries and SDK documentation. However this is in a *closed format*, with no developer access to the source files, so anyone developing ESP8266 applications must rely solely on the SDK API (and the somewhat Spartan SDK API documentation). (Note that for the ESP32, Espressif have moved to an open-source approach for its ESP-IDF.)
|
||||
The ESP8266 was designed and is fabricated in China by [Espressif Systems](http://espressif.com/new-sdk-release/). Espressif have also developed and released a companion software development kit (SDK) to enable developers to build practical IoT applications for the ESP8266. The SDK is made freely available to developers in the form of binary libraries and SDK documentation. However this is in a *closed format*, with no developer access to the source files, so anyone developing ESP8266 applications must rely solely on the SDK API (and the somewhat Spartan SDK API documentation). (Note that for the ESP32, Espressif have moved to an open-source approach for its ESP-IDF.)
|
||||
|
||||
The NodeMCU Lua firmware is an ESP8266 application and must therefore be layered over the ESP8266 SDK. However, the hooks and features of Lua enable it to be seamlessly integrated without losing any of the standard Lua language features. The firmware has replaced some standard Lua modules that don't align well with the SDK structure with ESP8266-specific versions. For example, the standard `io` and `os` libraries don't work, but have been largely replaced by the NodeMCU `node` and `file` libraries. The `debug` and `math` libraries have also been omitted to reduce the runtime footprint (`modulo` can be done via `%`, `power` via `^`).
|
||||
The NodeMCU Lua firmware is an ESP8266 application and must therefore be layered over the ESP8266 SDK. However, the hooks and features of Lua enable it to be seamlessly integrated without losing any of the standard Lua language features. The firmware has replaced some standard Lua modules that don't align well with the SDK structure with ESP8266-specific versions. For example, the standard `io` and `os` libraries don't work, but have been largely replaced by the NodeMCU `node` and `file` libraries. The `debug` and `math` libraries have also been omitted to reduce the runtime footprint (`modulo` can be done via `%`, `power` via `^`).
|
||||
|
||||
NodeMCU Lua is based on [eLua](http://www.eluaproject.net/overview), a fully featured implementation of Lua 5.1 that has been optimized for embedded system development and execution to provide a scripting framework that can be used to deliver useful applications within the limited RAM and Flash memory resources of embedded processors such as the ESP8266. One of the main changes introduced in the eLua fork is to use read-only tables and constants wherever practical for library modules. On a typical build this approach reduces the RAM footprint by some 20-25KB and this makes a Lua implementation for the ESP8266 feasible. This technique is called LTR and this is documented in detail in an eLua technical paper: [Lua Tiny RAM](http://www.eluaproject.net/doc/master/en_arch_ltr.html).
|
||||
NodeMCU Lua is based on [eLua](http://www.eluaproject.net/overview), a fully featured implementation of Lua 5.1 that has been optimized for embedded system development and execution to provide a scripting framework that can be used to deliver useful applications within the limited RAM and Flash memory resources of embedded processors such as the ESP8266. One of the main changes introduced in the eLua fork is to use read-only tables and constants wherever practical for library modules. On a typical build this approach reduces the RAM footprint by some 20-25KB and this makes a Lua implementation for the ESP8266 feasible. This technique is called LTR and this is documented in detail in an eLua technical paper: [Lua Tiny RAM](http://www.eluaproject.net/doc/master/en_arch_ltr.html).
|
||||
|
||||
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
|
||||
|
||||
### 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.
|
||||
* The main standard Lua libraries -- `core`, `coroutine`, `string` and `table` are implemented.
|
||||
- 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.
|
||||
|
||||
### 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.
|
||||
* 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 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.
|
||||
* 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.
|
||||
* 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.
|
||||
* 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.
|
||||
* 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.
|
||||
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 details 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 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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
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?
|
||||
|
||||
* 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.
|
||||
* 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..
|
||||
* 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.
|
||||
* 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 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.
|
||||
- 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..
|
||||
- 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.
|
||||
- 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.
|
||||
* 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.
|
||||
* 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.
|
||||
- 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/
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
* _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?
|
||||
|
||||
|
@ -129,236 +143,259 @@ SDK Callbacks include:
|
|||
| mqtt | `client:m:on(event, function(conn[, topic, data])` |
|
||||
| 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?
|
||||
|
||||
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
|
||||
```Lua
|
||||
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**..
|
||||
* 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
|
||||
do
|
||||
local tmr2func = function() ds.convert_T(true); tmr1:start() end
|
||||
tmr2:alarm(300000, tmr.ALARM_AUTO, tmr2func)
|
||||
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
|
||||
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
|
||||
|
||||
for reg, regAddr in pairs {
|
||||
IODOR = 0x00,
|
||||
GPPU = 0x06, -- Pull-up resistors register for MCP23008
|
||||
GPIO = 0x09,
|
||||
OLAT = 0x0A,
|
||||
} do
|
||||
dev['write'..reg] = function(o, dataByte)
|
||||
write(MCP23008addr, regAddr, dataByte)
|
||||
end
|
||||
dev['read'..reg] = function(o)
|
||||
return read(MCP23008addr, regAddr)
|
||||
end
|
||||
```Lua
|
||||
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**.
|
||||
|
||||
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
|
||||
do
|
||||
local tmr2func = function() ds.convert_T(true); tmr1:start() end
|
||||
tmr2:alarm(300000, tmr.ALARM_AUTO, tmr2func)
|
||||
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 {
|
||||
IODOR = 0x00,
|
||||
GPPU = 0x06, -- Pull-up resistors register for MCP23008
|
||||
GPIO = 0x09,
|
||||
OLAT = 0x0A,
|
||||
} do
|
||||
dev['write' .. reg] = function(o, dataByte)
|
||||
write(MCP23008addr, regAddr, dataByte)
|
||||
end
|
||||
dev['read' .. reg] = function(o)
|
||||
return read(MCP23008addr, regAddr)
|
||||
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.
|
||||
* 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.*
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
local i2c = i2c
|
||||
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
|
||||
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
|
||||
```
|
||||
* I will cover some useful Global and Upvalue techniques in later Qs.
|
||||
|
||||
### 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.
|
||||
* 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
* 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.
|
||||
* **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.
|
||||
- **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 **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.
|
||||
|
||||
### 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
|
||||
srv:on("connection", function(sck, c)
|
||||
svr:send(reply)
|
||||
svr:send(reply) -- should be 'sck' instead of 'srv'
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
### How do I track globals
|
||||
|
||||
* 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
|
||||
- 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.
|
||||
|
||||
### Why is it importance to understand how upvalues are implemented when programming for the ESP8266?
|
||||
|
||||
The use of upvalues is a core Lua feature. This is explained in detail in PiL. Any Lua routines defined within an outer scope my use them. This can include routines directly or indirectly referenced in the globals table, **_G**, or in the Lua Registry.
|
||||
The use of upvalues is a core Lua feature. This is explained in detail in PiL. Any Lua routines defined within an outer scope my use them. This can include routines directly or indirectly referenced in the globals table, **_G**, or in the Lua Registry.
|
||||
|
||||
The number of upvalues associated with a given routine is calculated during compile and a stack vector is allocated for them when the closure is bound to hold these references. Each upvalues is classed as open or closed. All upvalues are initially open which means that the upvalue references back to the outer function's register set. However, upvalues must be able to outlive the scope of the outer routine where they are declared as a local variable. The runtime VM does this by adding extra checks when executing a function return to scan any defined closures within its scope for back references and allocate memory to hold the upvalue and points the upvalue's reference to this. This is known as a closed upvalue.
|
||||
The number of upvalues associated with a given routine is calculated during compile and a stack vector is allocated for them when the closure is bound to hold these references. Each upvalues is classed as open or closed. All upvalues are initially open which means that the upvalue references back to the outer function's register set. However, upvalues must be able to outlive the scope of the outer routine where they are declared as a local variable. The runtime VM does this by adding extra checks when executing a function return to scan any defined closures within its scope for back references and allocate memory to hold the upvalue and points the upvalue's reference to this. This is known as a closed upvalue.
|
||||
|
||||
This processing is a mature part of the Lua 5.x runtime system, and for normal Lua applications development this "behind-the-scenes" magic ensures that upvalues just work as any programmer might expect. Sufficient garbage collector metadata is also stored so that these hidden values will be garbage collected correctly *when properly dereferenced*.
|
||||
This processing is a mature part of the Lua 5.x runtime system, and for normal Lua applications development this "behind-the-scenes" magic ensures that upvalues just work as any programmer might expect. Sufficient garbage collector metadata is also stored so that these hidden values will be garbage collected correctly *when properly dereferenced*.
|
||||
|
||||
One further complication is that some library functions don't implicitly dereference expired callback references and as a result their upvalues may not be garbage collected and this application error can be be manifested as a memory leak. So using upvalues can cause more frequent and difficult to diagnose PANICs during testing. So my general recommendation is still to stick to globals during initial development, and explicitly dereference resources by setting them to `nil` when you have done with them.
|
||||
|
||||
### Can I encapsulate actions such as sending an email in a Lua function?
|
||||
|
||||
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
|
||||
-- prepare message
|
||||
status = mail.send(to, subject, body)
|
||||
-- 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
|
||||
-- prepare message
|
||||
local ms = require("mail_sender")
|
||||
return ms.send(to, subject, body, function(status)
|
||||
loadfile("process_next.lua")(status)
|
||||
end)
|
||||
-- prepare message
|
||||
local ms = require("mail_sender")
|
||||
return ms.send(to, subject, body, function(status)
|
||||
loadfile("process_next.lua")(status)
|
||||
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()?
|
||||
|
||||
If you are used coding in a procedural paradigm then it is understandable that you consider using [`tmr.delay()`](modules/tmr.md#tmrdelay) to time sequence your application. However as discussed in the previous section, with NodeMCU Lua you are coding in an event-driven paradigm.
|
||||
If you are used coding in a procedural paradigm then it is understandable that you consider using [`tmr.delay()`](modules/tmr.md#tmrdelay) to time sequence your application. However as discussed in the previous section, with NodeMCU Lua you are coding in an event-driven paradigm.
|
||||
|
||||
If you look at the `app/modules/tmr.c` code for this function, then you will see that it executes a low level `ets_delay_us(delay)`. This function isn't part of the NodeMCU code or the SDK; it's actually part of the xtensa-lx106 boot ROM, and is a simple timing loop which polls against the internal CPU clock. `tmr.delay()` is really intended to be used where you need to have more precise timing control on an external hardware I/O (e.g. lifting a GPIO pin high for 20 μSec). It does this with interrupts enabled, because so there is no guarantee that the delay will be as requested, and the Lua RTS itself may inject operations such as GC, so if you do this level of precise control then you should encode your application as a C library.
|
||||
If you look at the `app/modules/tmr.c` code for this function, then you will see that it executes a low level `ets_delay_us(delay)`. This function isn't part of the NodeMCU code or the SDK; it's actually part of the xtensa-lx106 boot ROM, and is a simple timing loop which polls against the internal CPU clock. `tmr.delay()` is really intended to be used where you need to have more precise timing control on an external hardware I/O (e.g. lifting a GPIO pin high for 20 μSec). It does this with interrupts enabled, because so there is no guarantee that the delay will be as requested, and the Lua RTS itself may inject operations such as GC, so if you do this level of precise control then you should encode your application as a C library.
|
||||
|
||||
It will achieve no functional purpose in pretty much every other usecase, as any other system code-based activity will be blocked from execution; at worst it will break your application and create hard-to-diagnose timeout errors. We therefore deprecate its general use.
|
||||
|
||||
### How do I avoid a PANIC loop in init.lua?
|
||||
|
||||
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.
|
||||
- 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.
|
||||
- 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.
|
||||
When this happens, the only robust solution is to reflash the firmware.
|
||||
|
||||
See ["Uploading code" → init.lua](upload.md#initlua) for an example.
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
* 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
|
||||
|
||||
### 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.
|
||||
* 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.
|
||||
* *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.*
|
||||
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.
|
||||
|
||||
_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
|
||||
|
||||
* 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.
|
||||
* 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/)
|
||||
* 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.
|
||||
* 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.
|
||||
- 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.
|
||||
- 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/)
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
|
||||
### 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.
|
||||
* 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.
|
||||
* 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:
|
||||
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.
|
||||
|
||||
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
|
||||
local s=net.createServer(net.TCP)
|
||||
s:listen(80,function(c) require("connector").init(c) end)
|
||||
local s = net.createServer(net.TCP)
|
||||
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
|
||||
local M, module = {}, ...
|
||||
...
|
||||
local M, module = {}, ......
|
||||
function M.init(csocket)
|
||||
package.loaded[module]=nil
|
||||
...
|
||||
package.loaded[module] = nil...
|
||||
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.
|
||||
* 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:
|
||||
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:
|
||||
|
||||
```lua
|
||||
--
|
||||
local s=net.createServer(net.TCP)
|
||||
s:listen(80,function(c) require("connector")(c) end)
|
||||
local s = net.createServer(net.TCP)
|
||||
s:listen(80, function(c) require("connector")(c) end)
|
||||
```
|
||||
|
||||
```lua
|
||||
local module = _ -- this is a situation where using an upvalue is essential!
|
||||
return function (csocket)
|
||||
package.loaded[module]=nil
|
||||
module = nil
|
||||
...
|
||||
local module = _ -- this is a situation where using an upvalue is essential!
|
||||
return function(csocket)
|
||||
package.loaded[module] = nil
|
||||
module = nil...
|
||||
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
|
||||
...
|
||||
local s=net.createServer(net.TCP)
|
||||
local connector = require("connector") -- don't do this unless you've got the RAM available!
|
||||
s:listen(80,connector)
|
||||
local s = net.createServer(net.TCP)
|
||||
local connector = require("connector") -- don't do this unless you've got the RAM available!
|
||||
s:listen(80, connector)
|
||||
```
|
||||
|
||||
### How do I reduce the size of my compiled code?
|
||||
|
||||
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.
|
||||
|
||||
### 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 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.
|
||||
* **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:
|
||||
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.
|
||||
- **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
|
||||
--- 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
|
|||
elseif a == "--interact" then
|
||||
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?
|
||||
|
||||
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?
|
||||
|
||||
* 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
|
||||
|
||||
### 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.
|
||||
|
||||
|
|
|
@ -363,30 +363,32 @@ Put NodeMCU in light sleep mode to reduce current consumption.
|
|||
|
||||
* NodeMCU can not enter light sleep mode if wifi is suspended.
|
||||
* All active timers will be suspended and then resumed when NodeMCU wakes from sleep.
|
||||
* Any previously suspended timers will be resumed when NodeMCU wakes from sleep.
|
||||
|
||||
!!! attention
|
||||
This is disabled by default. Modify `PMSLEEP_ENABLE` in `app/include/user_config.h` to enable it.
|
||||
|
||||
#### Syntax
|
||||
`node.sleep({wake_gpio[, duration, int_type, resume_cb, preserve_mode]})`
|
||||
<!---`node.sleep({wake_pin[, duration, int_type, resume_cb, preserve_mode]})`--->
|
||||
`node.sleep({wake_pin[, int_type, resume_cb, preserve_mode]})`
|
||||
|
||||
#### Parameters
|
||||
- `duration` Sleep duration in microseconds(μs). If a sleep duration of `0` is specified, suspension will be indefinite (Range: 0 or 50000 - 268435454 μs (0:4:28.000454))
|
||||
- `wake_pin` 1-12, pin to attach wake interrupt to. Note that pin 0(GPIO 16) does not support interrupts.
|
||||
- If sleep duration is indefinite, `wake_pin` must be specified
|
||||
- Please refer to the [`GPIO module`](gpio.md) for more info on the pin map.
|
||||
- `int_type` type of interrupt that you would like to wake on. (Optional, Default: `node.INT_LOW`)
|
||||
- valid interrupt modes:
|
||||
- `node.INT_UP` Rising edge
|
||||
- `node.INT_DOWN` Falling edge
|
||||
- `node.INT_BOTH` Both edges
|
||||
- `node.INT_LOW` Low level
|
||||
- `node.INT_HIGH` High level
|
||||
- `resume_cb` Callback to execute when WiFi wakes from suspension. (Optional)
|
||||
- `preserve_mode` preserve current WiFi mode through node sleep. (Optional, Default: true)
|
||||
- If true, Station and StationAP modes will automatically reconnect to previously configured Access Point when NodeMCU resumes.
|
||||
- If false, discard WiFi mode and leave NodeMCU in `wifi.NULL_MODE`. WiFi mode will be restored to original mode on restart.
|
||||
<!--- timed light_sleep currently does not work, the 'duration' parameter is here as a place holder--->
|
||||
<!--- * `duration` Sleep duration in microseconds(μs). If a sleep duration of `0` is specified, suspension will be indefinite (Range: 0 or 50000 - 268435454 μs (0:4:28.000454))--->
|
||||
|
||||
* `wake_pin` 1-12, pin to attach wake interrupt to. Note that pin 0(GPIO 16) does not support interrupts.
|
||||
<!---* If sleep duration is indefinite, `wake_pin` must be specified--->
|
||||
* Please refer to the [`GPIO module`](gpio.md) for more info on the pin map.
|
||||
* `int_type` type of interrupt that you would like to wake on. (Optional, Default: `node.INT_LOW`)
|
||||
* valid interrupt modes:
|
||||
* `node.INT_UP` Rising edge
|
||||
* `node.INT_DOWN` Falling edge
|
||||
* `node.INT_BOTH` Both edges
|
||||
* `node.INT_LOW` Low level
|
||||
* `node.INT_HIGH` High level
|
||||
* `resume_cb` Callback to execute when WiFi wakes from suspension. (Optional)
|
||||
* `preserve_mode` preserve current WiFi mode through node sleep. (Optional, Default: true)
|
||||
* If true, Station and StationAP modes will automatically reconnect to previously configured Access Point when NodeMCU resumes.
|
||||
* If false, discard WiFi mode and leave NodeMCU in `wifi.NULL_MODE`. WiFi mode will be restored to original mode on restart.
|
||||
|
||||
#### Returns
|
||||
- `nil`
|
||||
|
@ -409,15 +411,15 @@ Put NodeMCU in light sleep mode to reduce current consumption.
|
|||
cfg.preserve_mode=false
|
||||
|
||||
node.sleep(cfg)
|
||||
|
||||
```
|
||||
<!---
|
||||
--Put NodeMCU in light sleep mode for 10 seconds with resume callback
|
||||
cfg={}
|
||||
cfg.duration=10*1000*1000
|
||||
cfg.resume_cb=function() print("WiFi resume") end
|
||||
|
||||
node.sleep(cfg)
|
||||
|
||||
```
|
||||
--->
|
||||
|
||||
#### See also
|
||||
- [`wifi.suspend()`](wifi.md#wifisuspend)
|
||||
|
@ -514,7 +516,7 @@ provides more detailed information on the EGC.
|
|||
- `mode`
|
||||
- `node.egc.NOT_ACTIVE` EGC inactive, no collection cycle will be forced in low memory situations
|
||||
- `node.egc.ON_ALLOC_FAILURE` Try to allocate a new block of memory, and run the garbage collector if the allocation fails. If the allocation fails even after running the garbage collector, the allocator will return with error.
|
||||
- `node.egc.ON_MEM_LIMIT` Run the garbage collector when the memory used by the Lua script goes beyond an upper `limit`. If the upper limit can't be satisfied even after running the garbage collector, the allocator will return with error.
|
||||
- `node.egc.ON_MEM_LIMIT` Run the garbage collector when the memory used by the Lua script goes beyond an upper `limit`. If the upper limit can't be satisfied even after running the garbage collector, the allocator will return with error. If the given limit is negative, it is interpreted as the desired amount of heap which should be left available. Whenever the free heap (as reported by `node.heap()` falls below the requested limit, the garbage collector will be run.
|
||||
- `node.egc.ALWAYS` Run the garbage collector before each memory allocation. If the allocation fails even after running the garbage collector, the allocator will return with error. This mode is very efficient with regards to memory savings, but it's also the slowest.
|
||||
- `level` in the case of `node.egc.ON_MEM_LIMIT`, this specifies the memory limit.
|
||||
|
||||
|
@ -525,6 +527,23 @@ provides more detailed information on the EGC.
|
|||
|
||||
`node.egc.setmode(node.egc.ALWAYS, 4096) -- This is the default setting at startup.`
|
||||
`node.egc.setmode(node.egc.ON_ALLOC_FAILURE) -- This is the fastest activeEGC mode.`
|
||||
`node.egc.setmode(node.egc.ON_MEM_LIMIT, 30720) -- Only allow the Lua runtime to allocate at most 30k, collect garbage if limit is about to be hit`
|
||||
`node.egc.setmode(node.egc.ON_MEM_LIMIT, -6144) -- Try to keep at least 6k heap available for non-Lua use (e.g. network buffers)`
|
||||
|
||||
|
||||
## node.egc.meminfo()
|
||||
|
||||
Returns memory usage information for the Lua runtime.
|
||||
|
||||
####Syntax
|
||||
`total_allocated, estimated_used = node.egc.meminfo()`
|
||||
|
||||
#### Parameters
|
||||
None.
|
||||
|
||||
#### Returns
|
||||
- `total_allocated` The total number of bytes allocated by the Lua runtime. This is the number which is relevant when using the `node.egc.ON_MEM_LIMIT` option with positive limit values.
|
||||
- `estimated_used` This value shows the estimated usage of the allocated memory.
|
||||
|
||||
# node.task module
|
||||
|
||||
|
|
|
@ -16,6 +16,11 @@ Attempts to obtain time synchronization.
|
|||
For best results you may want to to call this periodically in order to compensate for internal clock drift. As stated in the [rtctime](rtctime.md) module documentation it's advisable to sync time after deep sleep and it's necessary to sync after module reset (add it to [`init.lua`](../upload.md#initlua) after WiFi initialization).
|
||||
Note that either a single server can be provided as an argument (name or address), or a list (table) of servers can be provided.
|
||||
|
||||
If *all* of the supplied host names/addresses are invalid, then the error callback will be called with argument type 1. Otherwise, if
|
||||
there is at least one valid name/address, then then sync will be performed.
|
||||
|
||||
If any sync operation fails (maybe the device is disconnected from the internet), then all the names will be looked up again.
|
||||
|
||||
#### Syntax
|
||||
`sntp.sync([server_ip], [callback], [errcallback], [autorepeat])`
|
||||
`sntp.sync({ server1, server2, .. }, [callback], [errcallback], [autorepeat])`
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
|
||||
This module provides a simple interface to [TCS34725 colour/light sensors](https://www.adafruit.com/product/1334) (Adafruit).
|
||||
|
||||
Note that you must call [`setup()`](#tcs34725setup) before you can start reading values!
|
||||
!!! Warning
|
||||
|
||||
You must call [`setup()`](#tcs34725setup) before you can start reading values!
|
||||
|
||||
## tcs34725.setup()
|
||||
|
||||
setupializes module. setupialization is mandatory before values can be read.
|
||||
Initialization via this call is mandatory before values can be read.
|
||||
|
||||
#### Syntax
|
||||
|
||||
|
|
|
@ -62,11 +62,9 @@ Functions supported in timer object:
|
|||
- [`t:alarm()`](#tmralarm)
|
||||
- [`t:interval()`](#tmrinterval)
|
||||
- [`t:register()`](#tmrregister)
|
||||
- [`t:resume()`](#tmrresume)
|
||||
- [`t:start()`](#tmrstart)
|
||||
- [`t:state()`](#tmrstate)
|
||||
- [`t:stop()`](#tmrstop)
|
||||
- [`t:suspend()`](#tmrsuspend)
|
||||
- [`t:unregister()`](#tmrunregister)
|
||||
|
||||
#### Parameters
|
||||
|
@ -184,61 +182,6 @@ mytimer:start()
|
|||
- [`tmr.create()`](#tmrcreate)
|
||||
- [`tmr.alarm()`](#tmralarm)
|
||||
|
||||
## tmr.resume()
|
||||
|
||||
Resume an individual timer.
|
||||
|
||||
Resumes a timer that has previously been suspended with either `tmr.suspend` or `tmr.suspend_all`
|
||||
|
||||
#### Syntax
|
||||
`tmr.resume(id/ref)`
|
||||
|
||||
#### Parameters
|
||||
`id/ref` timer id (0-6) or object, obsolete for OO API (→ [`tmr.create()`](#tmrcreate))
|
||||
|
||||
#### Returns
|
||||
`true` if timer was resumed successfully
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
--resume timer mytimer
|
||||
mytimer:resume()
|
||||
|
||||
--alternate metod
|
||||
tmr.resume(mytimer)
|
||||
|
||||
```
|
||||
#### See also
|
||||
- [`tmr.suspend()`](#tmrsuspend)
|
||||
- [`tmr.suspend_all()`](#tmrsuspendall)
|
||||
- [`tmr.resume_all()`](#tmrresumeall)
|
||||
|
||||
## tmr.resume_all()
|
||||
|
||||
Resume all timers.
|
||||
|
||||
Resumes all timers including those previously been suspended with either `tmr.suspend` or `tmr.suspend_all`
|
||||
|
||||
#### Syntax
|
||||
`tmr.resume_all()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`true` if timers were resumed successfully
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
--resume all previously suspended timers
|
||||
tmr.resume_all()
|
||||
|
||||
```
|
||||
#### See also
|
||||
- [`tmr.suspend()`](#tmrsuspend)
|
||||
- [`tmr.suspend_all()`](#tmrsuspendall)
|
||||
- [`tmr.resume()`](#tmrresume)
|
||||
|
||||
## tmr.softwd()
|
||||
|
||||
Provides a simple software watchdog, which needs to be re-armed or disabled before it expires, or the system will be restarted.
|
||||
|
@ -336,72 +279,6 @@ if not mytimer:stop() then print("timer not stopped, not registered?") end
|
|||
- [`tmr.stop()`](#tmrstop)
|
||||
- [`tmr.unregister()`](#tmrunregister)
|
||||
|
||||
## tmr.suspend()
|
||||
|
||||
Suspend an armed timer.
|
||||
|
||||
!!! attention
|
||||
This is disabled by default. Modify `ENABLE_TIMER_SUSPEND` in `app/include/user_config.h` to enable it.
|
||||
|
||||
* Timers can be suspended at any time after they are armed.
|
||||
* If a timer is rearmed with `tmr.start` or `tmr.alarm` any matching suspended timers will be discarded.
|
||||
|
||||
#### Syntax
|
||||
`tmr.suspend(id/ref)`
|
||||
|
||||
#### Parameters
|
||||
`id/ref` timer id (0-6) or object, obsolete for OO API (→ [`tmr.create()`](#tmrcreate))
|
||||
|
||||
#### Returns
|
||||
`true` if timer was resumed successfully
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
--suspend timer mytimer
|
||||
mytimer:suspend()
|
||||
|
||||
--alternate metod
|
||||
tmr.suspend(mytimer)
|
||||
|
||||
```
|
||||
#### See also
|
||||
- [`tmr.suspend_all()`](#tmrsuspendall)
|
||||
- [`tmr.resume()`](#tmrresume)
|
||||
- [`tmr.resume_all()`](#tmrresumeall)
|
||||
|
||||
|
||||
## tmr.suspend_all()
|
||||
|
||||
Suspend all currently armed timers.
|
||||
|
||||
!!! attention
|
||||
This is disabled by default. Modify `ENABLE_TIMER_SUSPEND` in `app/include/user_config.h` to enable it.
|
||||
|
||||
!!! Warning
|
||||
This function suspends ALL active timers, including any active timers started by the NodeMCU subsystem or other modules. this may cause parts of your program to stop functioning properly.
|
||||
USE THIS FUNCTION AT YOUR OWN RISK!
|
||||
|
||||
#### Syntax
|
||||
`tmr.suspend_all()`
|
||||
|
||||
#### Parameters
|
||||
none
|
||||
|
||||
#### Returns
|
||||
`true` if timers were suspended successfully
|
||||
|
||||
#### Example
|
||||
```lua
|
||||
--suspend timer mytimer
|
||||
tmr.suspend_all()
|
||||
|
||||
```
|
||||
#### See also
|
||||
- [`tmr.suspendl()`](#tmrsuspend)
|
||||
- [`tmr.resume()`](#tmrresume)
|
||||
- [`tmr.resume_all()`](#tmrresumeall)
|
||||
|
||||
|
||||
## tmr.time()
|
||||
|
||||
Returns the system uptime, in seconds. Limited to 31 bits, after that it wraps around back to zero.
|
||||
|
|
|
@ -58,6 +58,36 @@ Gets the current WiFi channel.
|
|||
#### Returns
|
||||
current WiFi channel
|
||||
|
||||
## wifi.getcountry()
|
||||
|
||||
Get the current country info.
|
||||
|
||||
#### Syntax
|
||||
`wifi.getcountry()`
|
||||
|
||||
#### Parameters
|
||||
`nil`
|
||||
|
||||
#### Returns
|
||||
- `country_info` this table contains the current country info configuration
|
||||
- `country` Country code, 2 character string.
|
||||
- `start_ch` Starting channel.
|
||||
- `end_ch` Ending channel.
|
||||
- `policy` The policy parameter determines which country info configuration to use, country info given to station by AP or local configuration.
|
||||
- `0` Country policy is auto, NodeMCU will use the country info provided by AP that the station is connected to.
|
||||
- `1` Country policy is manual, NodeMCU will use locally configured country info.
|
||||
|
||||
#### Example
|
||||
|
||||
```lua
|
||||
for k, v in pairs(wifi.getcountry()) do
|
||||
print(k, v)
|
||||
end
|
||||
```
|
||||
|
||||
#### See also
|
||||
[`wifi.setcountry()`](#wifisetcountry)
|
||||
|
||||
## wifi.getdefaultmode()
|
||||
|
||||
Gets default WiFi operation mode.
|
||||
|
@ -165,6 +195,48 @@ wifi.resume(function() print("WiFi resume") end)
|
|||
- [`node.sleep()`](node.md#nodesleep)
|
||||
- [`node.dsleep()`](node.md#nodedsleep)
|
||||
|
||||
## wifi.setcountry()
|
||||
|
||||
Set the current country info.
|
||||
|
||||
#### Syntax
|
||||
`wifi.setcountry(country_info)`
|
||||
|
||||
#### Parameters
|
||||
- `country_info` This table contains the country info configuration. (If a blank table is passed to this function, default values will be configured.)
|
||||
- `country` Country code, 2 character string containing the country code (a list of country codes can be found [here](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements)). (Default:"CN")
|
||||
- `start_ch` Starting channel (range:1-14). (Default:1)
|
||||
- `end_ch` Ending channel, must not be less than starting channel (range:1-14). (Default:13)
|
||||
- `policy` The policy parameter determines which country info configuration to use, country info given to station by AP or local configuration. (default:`wifi.COUNTRY_AUTO`)
|
||||
- `wifi.COUNTRY_AUTO` Country policy is auto, NodeMCU will use the country info provided by AP that the station is connected to.
|
||||
- while in stationAP mode, beacon/probe respose will reflect the country info of the AP that the station is connected to.
|
||||
- `wifi.COUNTRY_MANUAL` Country policy is manual, NodeMCU will use locally configured country info.
|
||||
|
||||
#### Returns
|
||||
`true` If configuration was sucessful.
|
||||
|
||||
#### Example
|
||||
|
||||
```lua
|
||||
do
|
||||
country_info={}
|
||||
country_info.country="US"
|
||||
country_info.start_ch=1
|
||||
country_info.end_ch=13
|
||||
country_info.policy=wifi.COUNTRY_AUTO;
|
||||
wifi.setcountry(country_info)
|
||||
end
|
||||
|
||||
--compact version
|
||||
wifi.setcountry({country="US", start_ch=1, end_ch=13, policy=wifi.COUNTRY_AUTO})
|
||||
|
||||
--Set defaults
|
||||
wifi.setcountry({})
|
||||
```
|
||||
|
||||
#### See also
|
||||
[`wifi.getcountry()`](#wifigetcountry)
|
||||
|
||||
## wifi.setmode()
|
||||
|
||||
Configures the WiFi mode to use. NodeMCU can run in one of four WiFi modes:
|
||||
|
|
|
@ -116,13 +116,12 @@ SECTIONS
|
|||
/* *libcrypto.a:*(.literal .text) - tested that safe to keep in iROM */
|
||||
/* *libdriver.a:*(.literal .text) - not used anywhere in NodeMCU */
|
||||
/* *libespnow.a:*(.literal .text) - not used anywhere in NodeMCU */
|
||||
/* *libmesh.a:*(.literal .text) - not used anywhere in NodeMCU */
|
||||
/* *liblwip_536.a:*(.literal .text) - source-based library used instead */
|
||||
/* *libpwm.a:*(.literal .text) - our own implementation used instead */
|
||||
/* *libwpa.a:*(.literal .text) - tested that safe to keep in iROM */
|
||||
/* *libwps.a:*(.literal .text) - tested that safe to keep in iROM */
|
||||
|
||||
*(.iram.text .iram0.text)
|
||||
*(.iram.text .iram0.text .iram0.text.*)
|
||||
|
||||
*(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ Works:
|
|||
- entering the chip's to shutdown mode (350uA -> 5uA power consumption)
|
||||
- waking up the chip from shutdown
|
||||
|
||||
##Require
|
||||
## Require
|
||||
```lua
|
||||
LM92 = require("lm92")
|
||||
```
|
||||
|
@ -16,19 +16,19 @@ LM92 = nil
|
|||
package.loaded["lm92"]=nil
|
||||
```
|
||||
|
||||
##setup()
|
||||
####Description
|
||||
## setup()
|
||||
#### Description
|
||||
Setting the address for lm92.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
setup(sda, scl, address)
|
||||
|
||||
####Parameters
|
||||
#### Parameters
|
||||
address: 0x48~0x4b, i2c address (depends on tha A0~A1 pins)
|
||||
####Returns
|
||||
#### Returns
|
||||
nil
|
||||
|
||||
####Example
|
||||
#### Example
|
||||
```lua
|
||||
LM92 = require("lm92")
|
||||
gpio0 = 3
|
||||
|
@ -39,126 +39,126 @@ addr = 0x48
|
|||
i2c.setup(0, sda, scl, i2c.SLOW) -- call i2c.setup() only once
|
||||
LM92.setup(addr)
|
||||
```
|
||||
##getTemperature()
|
||||
####Description
|
||||
## getTemperature()
|
||||
#### Description
|
||||
Returns the temperature register's content.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
getTemperature()
|
||||
|
||||
####Parameters
|
||||
#### Parameters
|
||||
-
|
||||
|
||||
####Returns
|
||||
#### Returns
|
||||
Temperature in degree Celsius.
|
||||
|
||||
####Example
|
||||
#### Example
|
||||
```lua
|
||||
t = LM92.getTemperature()
|
||||
print("Got temperature: "..t.." C")
|
||||
```
|
||||
|
||||
##wakeup()
|
||||
####Description
|
||||
## wakeup()
|
||||
#### Description
|
||||
Makes the chip exit the low power shutdown mode.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
wakeup()
|
||||
|
||||
####Parameters
|
||||
#### Parameters
|
||||
-
|
||||
|
||||
####Returns
|
||||
#### Returns
|
||||
-
|
||||
|
||||
####Example
|
||||
#### Example
|
||||
```lua
|
||||
LM92.wakeup()
|
||||
tmr.delay( 1 * 1000 * 1000 )
|
||||
```
|
||||
|
||||
##shutdown()
|
||||
####Description
|
||||
## shutdown()
|
||||
#### Description
|
||||
Makes the chip enter the low power shutdown mode.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
shutdown()
|
||||
|
||||
####Parameters
|
||||
#### Parameters
|
||||
-
|
||||
|
||||
####Returns
|
||||
#### Returns
|
||||
-
|
||||
|
||||
####Example
|
||||
#### Example
|
||||
```lua
|
||||
LM92.shutdown()
|
||||
```
|
||||
|
||||
##setThyst()
|
||||
####Description
|
||||
## setThyst()
|
||||
#### Description
|
||||
Set hysteresis Temperature.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
setThyst(data_wr)
|
||||
|
||||
####Parameters
|
||||
#### Parameters
|
||||
data_wr: 130~-55 ºC, hysteresis Temperature
|
||||
|
||||
####Returns
|
||||
#### Returns
|
||||
nil
|
||||
|
||||
####Example
|
||||
#### Example
|
||||
```lua
|
||||
LM92.setThyst(3)
|
||||
```
|
||||
|
||||
##setTcrit()
|
||||
####Description
|
||||
## setTcrit()
|
||||
#### Description
|
||||
Set Critical Temperature.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
setTcrit(data_wr)
|
||||
|
||||
####Parameters
|
||||
#### Parameters
|
||||
data_wr: 130~-55 ºC, Critical Temperature
|
||||
|
||||
####Returns
|
||||
#### Returns
|
||||
nil
|
||||
|
||||
####Example
|
||||
#### Example
|
||||
```lua
|
||||
LM92.setTcrit(100.625)
|
||||
```
|
||||
|
||||
##setTlow()
|
||||
####Description
|
||||
## setTlow()
|
||||
#### Description
|
||||
Set Low Window Temperature.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
setTlow(data_wr)
|
||||
|
||||
####Parameters
|
||||
data_wr: 130~-55 ºC, Low Window Temperature
|
||||
|
||||
####Returns
|
||||
#### Returns
|
||||
nil
|
||||
|
||||
####Example
|
||||
#### Example
|
||||
```lua
|
||||
LM92.setTlow(32.25)
|
||||
```
|
||||
##setThigh()
|
||||
## setThigh()
|
||||
####Description
|
||||
Set High Window Temperature.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
setThigh(data_wr)
|
||||
|
||||
####Parameters
|
||||
#### Parameters
|
||||
data_wr: 130~-55 ºC, High Window Temperature
|
||||
|
||||
####Returns
|
||||
#### Returns
|
||||
nil
|
||||
|
||||
####Example
|
||||
|
@ -166,83 +166,84 @@ nil
|
|||
LM92.setThigh(27.5)
|
||||
```
|
||||
|
||||
##getThyst()
|
||||
####Description
|
||||
## getThyst()
|
||||
#### Description
|
||||
Get hysteresis Temperature.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
getThyst()
|
||||
|
||||
####Parameters
|
||||
#### Parameters
|
||||
--
|
||||
|
||||
####Returns
|
||||
#### Returns
|
||||
Hysteresis Temperature in degree Celsius.
|
||||
|
||||
####Example
|
||||
#### Example
|
||||
```lua
|
||||
t = LM92.getThyst()
|
||||
print("Got hysteresis temperature: "..t.." C")
|
||||
```
|
||||
|
||||
##getTcrit()
|
||||
####Description
|
||||
## getTcrit()
|
||||
#### Description
|
||||
Get Critical Temperature.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
getTcrit()
|
||||
|
||||
####Parameters
|
||||
#### Parameters
|
||||
--
|
||||
|
||||
####Returns
|
||||
#### Returns
|
||||
Critical Temperature in degree Celsius.
|
||||
|
||||
####Example
|
||||
#### Example
|
||||
```lua
|
||||
t = LM92.getTcrit()
|
||||
print("Got Critical temperature: "..t.." C")
|
||||
```
|
||||
|
||||
##getTlow()
|
||||
####Description
|
||||
## getTlow()
|
||||
#### Description
|
||||
Get Low Window Temperature.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
getTlow()
|
||||
|
||||
####Parameters
|
||||
#### Parameters
|
||||
--
|
||||
|
||||
####Returns
|
||||
#### Returns
|
||||
Low Window Temperature in degree Celsius.
|
||||
|
||||
####Example
|
||||
#### Example
|
||||
```lua
|
||||
t = LM92.getTlow()
|
||||
print("Got Low Window temperature: "..t.." C")
|
||||
```
|
||||
|
||||
##getThigh()
|
||||
####Description
|
||||
## getThigh()
|
||||
#### Description
|
||||
Get High Window Temperature.
|
||||
|
||||
####Syntax
|
||||
#### Syntax
|
||||
getThigh()
|
||||
|
||||
####Parameters
|
||||
#### Parameters
|
||||
--
|
||||
|
||||
####Returns
|
||||
#### Returns
|
||||
High Window Temperature in degree Celsius.
|
||||
|
||||
####Example
|
||||
#### Example
|
||||
```lua
|
||||
t = LM92.getThigh()
|
||||
print("Got High Window temperature: "..t.." C")
|
||||
```
|
||||
|
||||
##Full example
|
||||
## Full example
|
||||
```lua
|
||||
--node.compile("lm92.lua")
|
||||
LM92 = require("lm92")
|
||||
gpio0 = 3
|
||||
|
@ -270,6 +271,7 @@ t = LM92.getTlow()
|
|||
print("Got Low: "..t.." C")
|
||||
t = LM92.getThigh()
|
||||
print("Got High: "..t.." C")
|
||||
```
|
||||
|
||||
#### TODO:
|
||||
- add full support of the features, including interrupt and critical alert support
|
||||
|
|
|
@ -15,15 +15,4 @@ void call_user_start(void);
|
|||
|
||||
#include_next "osapi.h"
|
||||
|
||||
#ifdef ENABLE_TIMER_SUSPEND
|
||||
extern void swtmr_register(void* timer_ptr);
|
||||
#undef os_timer_arm
|
||||
#define os_timer_arm(timer_ptr, duration, mode) do{swtmr_register(timer_ptr); \
|
||||
ets_timer_arm_new(timer_ptr, duration, mode, 1);}while(0);
|
||||
|
||||
extern void swtmr_unregister(void* timer_ptr);
|
||||
#undef os_timer_disarm
|
||||
#define os_timer_disarm(timer_ptr) do{swtmr_unregister(timer_ptr); ets_timer_disarm(timer_ptr);}while(0);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue