diff --git a/.travis.yml b/.travis.yml
index 894f2fd0..5e76c51e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,6 @@ language: cpp
before_install:
- sudo apt-get install -y python-serial srecord
install:
-- wget https://github.com/GeorgeHahn/nodemcu-firmware/raw/travis/tools/esp-open-sdk.tar.gz -O tools/esp-open-sdk.tar.gz
- tar -zxvf tools/esp-open-sdk.tar.gz
- export PATH=$PATH:$PWD/esp-open-sdk/sdk:$PWD/esp-open-sdk/xtensa-lx106-elf/bin
script:
diff --git a/README.md b/README.md
index 295ea337..3c7d04f7 100644
--- a/README.md
+++ b/README.md
@@ -36,44 +36,41 @@ Tencent QQ group: 309957875
- cross compiler (done)
# Change log
+2015-06-27
+fixed ap/station-ap cannot connect to the device.
+added wifi.ap.getconfig().
+fixed net.dns.getdnsserver().
+added new base64 lua example.
+added node.bootreason() to inspect boot cause.
+optimization of u8g.
+
+# Change log
+2015-06-25
+move constants to ROM. Frees up 16k+ of RAM.
+add dhtlib for DHT11/21/22/33/44, port from Arduino.
+add 433MHz transmission.
+add crypto library.
+re-add ws2812.write().
+add wifi.getchannel.
+changed wifi_setip() to allow setting SoftAP gateway to 0.0.0.0.
+added net.dns.setdnsserver and net.dns.getdnsserver.
+add support for lm92 temperature sensor.
+implement getStrWidth() and setFontLineSpacingFactor().
+add -Os flag to release and debug builds.
+changed output format of table that is output by wifi_scan_done.
+added ability to set scan configuration to wifi.sta.getap.
+added function wifi.sta.getconfig().
+allow connecting to unsecured WiFi networks.
+add setphymode and getphymode to wifi module.
+add multicastJoin and multicastLeave to net module.
+add Yeelink Modules.
+
2015-03-31
polish mqtt module, add queue for mqtt module.
add reconnect option to mqtt.connect api, :connect( host, port, secure, auto_reconnect, function(client) )
move node.readvdd33 to adc.readvdd33.
tools/esptool.py supported NodeMCU devkit automatic flash.
-2015-03-18
-update u8glib.
-merge everything to master.
-
-2015-03-17
-add cjson module, only cjson.encode() and cjson.decode() is implemented.
-read doc [here](https://github.com/nodemcu/nodemcu-firmware/blob/master/app/cjson/manual.txt)
-
-2015-03-15
-bugs fixed: #239, #273.
-reduce coap module memory usage, add coap module to default built.
-
-2015-03-11
-fix bugs of spiffs.
-build both float and integer version [latest releases](https://github.com/nodemcu/nodemcu-firmware/releases/latest).
-fix tmr.time().
-fix memory leak when DNS fail.
-
-2015-03-10
-update to the recent spiffs.
-add file.fsinfo() api, usage: remain, used, total = file.fsinfo().
-add Travis CI. please download the latest firmware from [releases](https://github.com/nodemcu/nodemcu-firmware/releases).
-add math lib, partial api work.
-u8g module, ws2812 module default enabled in dev-branch build.
-
-2015-02-13
-add node.compile() api to compile lua text file into lua bytecode file.
-this will reduce memory usage noticeably when require modules into NodeMCU.
-raise internal LUA_BUFFERSIZE from 1024 to 4096.
-lua require("mod") will load "mod.lc" file first if exist.
-build latest pre_build bin.
-
[more change log](https://github.com/nodemcu/nodemcu-firmware/wiki)
##GPIO NEW TABLE ( Build 20141219 and later)
@@ -145,6 +142,11 @@ build latest pre_build bin.
#define LUA_USE_MODULES_CJSON
#endif /* LUA_USE_MODULES */
```
+#Online firmware custom build
+
+For many application, some modules are not used, remove them can free many memory.
+
+Please try Marcel's [NodeMCU custom builds](http://frightanic.com/nodemcu-custom-build) cloud service and you can get your own firmware.
#Flash the firmware
nodemcu_latest.bin: 0x00000
@@ -160,6 +162,13 @@ Or, if you build your own bin from source code.
#Connect the hardware in serial
baudrate:9600
+#Write Lua script to filesystem
+####Esplorer
+Victor Brutskiy's [Esplorer](https://github.com/4refr0nt/ESPlorer) support most platforms such as Linux, Windows, Mac OS, etc. This software is opensource and can write lua/lc files to filesystem.
+
+####NodeMCU Studio
+[NodeMCU Studio](https://github.com/nodemcu/nodemcu-studio-csharp) is written in C# and support Windows. This software is opensource and can write lua files to filesystem.
+
#Start play
####Connect to your ap
diff --git a/app/Makefile b/app/Makefile
index 87331c24..8275a264 100644
--- a/app/Makefile
+++ b/app/Makefile
@@ -99,7 +99,7 @@ LINKFLAGS_eagle.app.v6 = \
-nostdlib \
-T$(LD_FILE) \
-Wl,--no-check-sections \
- -u call_user_start \
+ -Wl,--wrap=_xtos_set_exception_handler \
-Wl,-static \
-Wl,--start-group \
-lc \
diff --git a/app/dhtlib/dht.c b/app/dhtlib/dht.c
index 26255219..7ab6db80 100644
--- a/app/dhtlib/dht.c
+++ b/app/dhtlib/dht.c
@@ -252,6 +252,8 @@ int dht_readSensor(uint8_t pin, uint8_t wakeupDelay)
DIRECT_WRITE_LOW(pin);
// delay(wakeupDelay);
for (i = 0; i < wakeupDelay; i++) os_delay_us(1000);
+ // Disable interrupts
+ os_intr_lock();
// digitalWrite(pin, HIGH); // T-go
DIRECT_WRITE_HIGH(pin);
os_delay_us(40);
@@ -303,6 +305,8 @@ int dht_readSensor(uint8_t pin, uint8_t wakeupDelay)
idx++;
}
}
+ // Enable interrupts
+ os_intr_unlock();
// pinMode(pin, OUTPUT);
DIRECT_MODE_OUTPUT(pin);
// digitalWrite(pin, HIGH);
diff --git a/app/include/rom.h b/app/include/rom.h
index 88d7c81d..de031179 100644
--- a/app/include/rom.h
+++ b/app/include/rom.h
@@ -37,3 +37,60 @@ extern unsigned char * base64_decode(const unsigned char *src, size_t len, size_
// initialized because it uses mem_malloc
extern void mem_init(void * start_addr);
+
+
+// 2, 3 = reset (module dependent?), 4 = wdt
+int rtc_get_reset_reason (void);
+
+// Hardware exception handling
+struct exception_frame
+{
+ uint32_t epc;
+ uint32_t ps;
+ uint32_t sar;
+ uint32_t unused;
+ union {
+ struct {
+ uint32_t a0;
+ // note: no a1 here!
+ uint32_t a2;
+ uint32_t a3;
+ uint32_t a4;
+ uint32_t a5;
+ uint32_t a6;
+ uint32_t a7;
+ uint32_t a8;
+ uint32_t a9;
+ uint32_t a10;
+ uint32_t a11;
+ uint32_t a12;
+ uint32_t a13;
+ uint32_t a14;
+ uint32_t a15;
+ };
+ uint32_t a_reg[15];
+ };
+ uint32_t cause;
+};
+
+/**
+ * C-level exception handler callback prototype.
+ *
+ * Does not need an RFE instruction - it is called through a wrapper which
+ * performs state capture & restore, as well as the actual RFE.
+ *
+ * @param ef An exception frame containing the relevant state from the
+ * exception triggering. This state may be manipulated and will
+ * be applied on return.
+ * @param cause The exception cause number.
+ */
+typedef void (*exception_handler_fn) (struct exception_frame *ef, uint32_t cause);
+
+/**
+ * Sets the exception handler function for a particular exception cause.
+ * @param handler The exception handler to install.
+ * If NULL, reverts to the XTOS default handler.
+ * @returns The previous exception handler, or NULL if none existed prior.
+ */
+exception_handler_fn _xtos_set_exception_handler (uint32_t cause, exception_handler_fn handler);
+
diff --git a/app/include/user_modules.h b/app/include/user_modules.h
index 9f10f8f3..00ec5ede 100644
--- a/app/include/user_modules.h
+++ b/app/include/user_modules.h
@@ -29,6 +29,7 @@
#define LUA_USE_MODULES_MQTT
#define LUA_USE_MODULES_COAP
#define LUA_USE_MODULES_U8G
+#define LUA_USE_MODULES_WS2801
#define LUA_USE_MODULES_WS2812
#define LUA_USE_MODULES_CJSON
#define LUA_USE_MODULES_CRYPTO
diff --git a/app/include/user_version.h b/app/include/user_version.h
index 789e5fea..40ce7eb4 100644
--- a/app/include/user_version.h
+++ b/app/include/user_version.h
@@ -7,6 +7,6 @@
#define NODE_VERSION_INTERNAL 0U
#define NODE_VERSION "NodeMCU 0.9.6"
-#define BUILD_DATE "build 20150618"
+#define BUILD_DATE "build 20150627"
#endif /* __USER_VERSION_H__ */
diff --git a/app/lwip/app/dhcpserver.c b/app/lwip/app/dhcpserver.c
index 81b9a3d4..410f14c9 100644
--- a/app/lwip/app/dhcpserver.c
+++ b/app/lwip/app/dhcpserver.c
@@ -13,7 +13,7 @@
#include "user_interface.h"
////////////////////////////////////////////////////////////////////////////////////
-static const uint8_t xid[4] = {0xad, 0xde, 0x12, 0x23};
+static uint8_t xid[4] = {0xad, 0xde, 0x12, 0x23};
static u8_t old_xid[4] = {0};
static const uint8_t magic_cookie[4] = {99, 130, 83, 99};
static struct udp_pcb *pcb_dhcps = NULL;
diff --git a/app/modules/modules.h b/app/modules/modules.h
index c9e55193..ad0c4cfa 100644
--- a/app/modules/modules.h
+++ b/app/modules/modules.h
@@ -133,6 +133,14 @@
#define ROM_MODULES_BIT
#endif
+#if defined(LUA_USE_MODULES_WS2801)
+#define MODULES_WS2801 "ws2801"
+#define ROM_MODULES_WS2801 \
+ _ROM(MODULES_WS2801, luaopen_ws2801, ws2801_map)
+#else
+#define ROM_MODULES_WS2801
+#endif
+
#if defined(LUA_USE_MODULES_WS2812)
#define MODULES_WS2812 "ws2812"
#define ROM_MODULES_WS2812 \
@@ -190,6 +198,7 @@
ROM_MODULES_UART \
ROM_MODULES_OW \
ROM_MODULES_BIT \
+ ROM_MODULES_WS2801 \
ROM_MODULES_WS2812 \
ROM_MODULES_CJSON \
ROM_MODULES_CRYPTO \
diff --git a/app/modules/net.c b/app/modules/net.c
index bb36d45b..4c91f44a 100644
--- a/app/modules/net.c
+++ b/app/modules/net.c
@@ -13,6 +13,7 @@
#include "c_types.h"
#include "mem.h"
#include "espconn.h"
+#include "lwip/dns.h"
#ifdef CLIENT_SSL_ENABLE
unsigned char *default_certificate;
@@ -224,7 +225,9 @@ static void net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
*/
// "enhanced"
+
lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->cb_dns_found_ref); // the callback function
+ lua_rawgeti(gL, LUA_REGISTRYINDEX, nud->self_ref); // pass the userdata(conn) to callback func in lua
if(ipaddr == NULL)
{
@@ -242,7 +245,7 @@ static void net_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
}
// "enhanced" end
- lua_call(gL, 1, 0);
+ lua_call(gL, 2, 0);
end:
if((pesp_conn->type == ESPCONN_TCP && pesp_conn->proto.tcp->remote_port == 0)
@@ -1445,14 +1448,16 @@ static int net_getdnsserver( lua_State* L )
if (numdns >= DNS_MAX_SERVERS)
return luaL_error( L, "server index out of range [0-%d]", DNS_MAX_SERVERS - 1);
- ip_addr_t ipaddr;
- dns_getserver(numdns,&ipaddr);
+ // ip_addr_t ipaddr;
+ // dns_getserver(numdns,&ipaddr);
+ // Bug fix by @md5crypt https://github.com/nodemcu/nodemcu-firmware/pull/500
+ ip_addr_t ipaddr = dns_getserver(numdns);
if ( ip_addr_isany(&ipaddr) ) {
lua_pushnil( L );
} else {
char temp[20] = {0};
- c_sprintf(temp, IPSTR, IP2STR( &ipaddr ) );
+ c_sprintf(temp, IPSTR, IP2STR( &ipaddr.addr ) );
lua_pushstring( L, temp );
}
diff --git a/app/modules/node.c b/app/modules/node.c
index 5f6ae9bd..ce8d29cd 100644
--- a/app/modules/node.c
+++ b/app/modules/node.c
@@ -414,6 +414,13 @@ static int node_setcpufreq(lua_State* L)
return 1;
}
+// Lua: code = bootreason()
+static int node_bootreason (lua_State *L)
+{
+ lua_pushnumber (L, rtc_get_reset_reason ());
+ return 1;
+}
+
// Module function map
#define MIN_OPT_LEVEL 2
#include "lrodefs.h"
@@ -438,6 +445,7 @@ const LUA_REG_TYPE node_map[] =
{ LSTRKEY( "CPU80MHZ" ), LNUMVAL( CPU80MHZ ) },
{ LSTRKEY( "CPU160MHZ" ), LNUMVAL( CPU160MHZ ) },
{ LSTRKEY( "setcpufreq" ), LFUNCVAL( node_setcpufreq) },
+ { LSTRKEY( "bootreason" ), LFUNCVAL( node_bootreason) },
// Combined to dsleep(us, option)
// { LSTRKEY( "dsleepsetoption" ), LFUNCVAL( node_deepsleep_setoption) },
#if LUA_OPTIMIZE_MEMORY > 0
diff --git a/app/modules/rc.c b/app/modules/rc.c
index 41ad6d3f..964896c4 100644
--- a/app/modules/rc.c
+++ b/app/modules/rc.c
@@ -9,13 +9,13 @@
#define defProtocol 1
#define defRepeat 10
#define defBits 24
-void transmit(int pin, int pulseLen, int nHighPulses, int nLowPulses) {
+static void ICACHE_FLASH_ATTR transmit(int pin, int pulseLen, int nHighPulses, int nLowPulses) {
platform_gpio_write(pin, 1);
os_delay_us(pulseLen*nHighPulses);
platform_gpio_write(pin, 0);
os_delay_us(pulseLen*nLowPulses);
}
-//rc.send(0,267715,24,185,1) --GPIO, code, bits, pulselen, protocol
+//rc.send(4,267715,24,185,1,10) --GPIO, code, bits, pulselen, protocol, repeat
static int ICACHE_FLASH_ATTR rc_send(lua_State* L) {
const uint8_t pin = luaL_checkinteger(L, 1);
platform_gpio_mode(pin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
diff --git a/app/modules/tmr.c b/app/modules/tmr.c
old mode 100644
new mode 100755
index 65007778..8e9bc852
--- a/app/modules/tmr.c
+++ b/app/modules/tmr.c
@@ -1,232 +1,358 @@
-// Module for interfacing with timer
+/*guys, srsly, turn on warnings in the makefile*/
+#if defined(__GNUC__)
+#pragma GCC diagnostic warning "-Wall"
+#pragma GCC diagnostic warning "-Wextra"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+/*-------------------------------------
+NEW TIMER API
+---------------------------------------
+
+tmr.wdclr() -- not changed
+tmr.now() -- not changed
+tmr.time() -- not changed
+tmr.delay() -- not changed
+tmr.alarm() -- not changed
+tmr.stop() -- changed, see below. use tmr.unregister for old functionality
+
+tmr.register(id, interval, mode, function)
+ bind function with timer and set the interval in ms
+ the mode can be:
+ tmr.ALARM_SINGLE for a single run alarm
+ tmr.ALARM_SEMI for a multiple single run alarm
+ tmr.ALARM_AUTO for a repating alarm
+ tmr.register does NOT start the timer
+ tmr.alarm is a tmr.register & tmr.start macro
+tmr.unregister(id)
+ stop alarm, unbind function and clean up memory
+ not needed for ALARM_SINGLE, as it unregisters itself
+tmr.start(id)
+ ret: bool
+ start a alarm, returns true on success
+tmr.stop(id)
+ ret: bool
+ stops a alarm, returns true on success
+ this call dose not free any memory, to do so use tmr.unregister
+ stopped alarms can be started with start
+tmr.interval(id, interval)
+ set alarm interval, running alarm will be restarted
+tmr.state(id)
+ ret: (bool, int) or nil
+ returns alarm status (true=started/false=stopped) and mode
+ nil if timer is unregistered
+tmr.softwd(int)
+ set a negative value to stop the timer
+ any other value starts the timer, when the
+ countdown reaches zero, the device restarts
+ the timer units are seconds
+*/
+
+#define MIN_OPT_LEVEL 2
-//#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "platform.h"
#include "auxmods.h"
#include "lrotable.h"
-
+#include "lrodefs.h"
#include "c_types.h"
-static os_timer_t alarm_timer[NUM_TMR];
-static int alarm_timer_cb_ref[NUM_TMR] = {LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF,LUA_NOREF};
-static bool alarm_timer_repeat[NUM_TMR]= {0,0,0,0,0,0,0};
+#define TIMER_MODE_OFF 3
+#define TIMER_MODE_SINGLE 0
+#define TIMER_MODE_SEMI 2
+#define TIMER_MODE_AUTO 1
+#define TIMER_IDLE_FLAG (1<<7)
-void alarm_timer_common(lua_State* L, unsigned id){
- if(alarm_timer_cb_ref[id] == LUA_NOREF)
- return;
- lua_rawgeti(L, LUA_REGISTRYINDEX, alarm_timer_cb_ref[id]);
- if(alarm_timer_repeat[id]==0)
- {
- if(alarm_timer_cb_ref[id] != LUA_NOREF)
- luaL_unref(L, LUA_REGISTRYINDEX, alarm_timer_cb_ref[id]);
+//well, the following are my assumptions
+//why, oh why is there no good documentation
+//chinese companies should learn from Atmel
+extern void ets_timer_arm_new(os_timer_t* t, uint32_t milliseconds, uint32_t repeat_flag, uint32_t isMstimer);
+extern void ets_timer_disarm(os_timer_t* t);
+extern void ets_timer_setfn(os_timer_t* t, os_timer_func_t *f, void *arg);
+extern void ets_delay_us(uint32_t us);
+extern uint32_t system_get_time();
+extern uint32_t platform_tmr_exists(uint32_t t);
+extern uint32_t system_rtc_clock_cali_proc();
+extern uint32_t system_get_rtc_time();
+extern void system_restart();
- }
- lua_call(L, 0, 0);
+//in fact lua_State is constant, it's pointless to pass it around
+//but hey, whatever, I'll just pass it, still we waste 28B here
+typedef struct{
+ os_timer_t os;
+ lua_State* L;
+ sint32_t lua_ref;
+ uint32_t interval;
+ uint8_t mode;
+}timer_struct_t;
+typedef timer_struct_t* timer_t;
+
+//everybody just love unions! riiiiight?
+static union {
+ uint64_t block;
+ uint32_t part[2];
+} rtc_time;
+static sint32_t soft_watchdog = -1;
+static timer_struct_t alarm_timers[NUM_TMR];
+static os_timer_t rtc_timer;
+
+static void alarm_timer_common(void* arg){
+ timer_t tmr = &alarm_timers[(uint32_t)arg];
+ if(tmr->lua_ref == LUA_NOREF || tmr->L == NULL)
+ return;
+ lua_rawgeti(tmr->L, LUA_REGISTRYINDEX, tmr->lua_ref);
+ //if the timer was set to single run we clean up after it
+ if(tmr->mode == TIMER_MODE_SINGLE){
+ luaL_unref(tmr->L, LUA_REGISTRYINDEX, tmr->lua_ref);
+ tmr->lua_ref = LUA_NOREF;
+ tmr->mode = TIMER_MODE_OFF;
+ }else if(tmr->mode == TIMER_MODE_SEMI){
+ tmr->mode |= TIMER_IDLE_FLAG;
+ }
+ lua_call(tmr->L, 0, 0);
}
-void alarm_timer_cb0(void *arg){
- if( !arg )
- return;
- alarm_timer_common((lua_State*)arg, 0);
+// Lua: tmr.delay( us )
+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 >= 1000000){
+ us -= 1000000;
+ os_delay_us(1000000);
+ WRITE_PERI_REG(0x60000914, 0x73);
+ }
+ if(us>0){
+ os_delay_us(us);
+ WRITE_PERI_REG(0x60000914, 0x73);
+ }
+ return 0;
}
-void alarm_timer_cb1(void *arg){
- if( !arg )
- return;
- alarm_timer_common((lua_State*)arg, 1);
+// Lua: tmr.now() , return system timer in us
+static int tmr_now(lua_State* L){
+ uint32_t now = 0x7FFFFFFF & system_get_time();
+ lua_pushinteger(L, now);
+ return 1;
}
-void alarm_timer_cb2(void *arg){
- if( !arg )
- return;
- alarm_timer_common((lua_State*)arg, 2);
+// Lua: tmr.register( id, interval, mode, function )
+static int tmr_register(lua_State* L){
+ uint32_t id = luaL_checkinteger(L, 1);
+ MOD_CHECK_ID(tmr, id);
+ sint32_t interval = luaL_checkinteger(L, 2);
+ uint8_t mode = luaL_checkinteger(L, 3);
+ //validate arguments
+ uint8_t args_valid = interval <= 0
+ || (mode != TIMER_MODE_SINGLE && mode != TIMER_MODE_SEMI && mode != TIMER_MODE_AUTO)
+ || (lua_type(L, 4) != LUA_TFUNCTION && lua_type(L, 4) != LUA_TLIGHTFUNCTION);
+ if(args_valid)
+ return luaL_error(L, "wrong arg range");
+ //get the lua function reference
+ lua_pushvalue(L, 4);
+ sint32_t ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ timer_t tmr = &alarm_timers[id];
+ if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
+ ets_timer_disarm(&tmr->os);
+ //there was a bug in this part, the second part of the following condition was missing
+ if(tmr->lua_ref != LUA_NOREF && tmr->lua_ref != ref)
+ luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
+ tmr->lua_ref = ref;
+ tmr->mode = mode|TIMER_IDLE_FLAG;
+ tmr->interval = interval;
+ tmr->L = L;
+ ets_timer_setfn(&tmr->os, alarm_timer_common, (void*)id);
+ return 0;
}
-void alarm_timer_cb3(void *arg){
- if( !arg )
- return;
- alarm_timer_common((lua_State*)arg, 3);
+// Lua: tmr.start( id )
+static int tmr_start(lua_State* L){
+ uint8_t id = luaL_checkinteger(L, 1);
+ MOD_CHECK_ID(tmr,id);
+ timer_t tmr = &alarm_timers[id];
+ //we return false if the timer is not idle
+ if(!(tmr->mode&TIMER_IDLE_FLAG)){
+ lua_pushboolean(L, 0);
+ }else{
+ tmr->mode &= ~TIMER_IDLE_FLAG;
+ ets_timer_arm_new(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO, 1);
+ lua_pushboolean(L, 1);
+ }
+ return 1;
}
-void alarm_timer_cb4(void *arg){
- if( !arg )
- return;
- alarm_timer_common((lua_State*)arg, 4);
+// Lua: tmr.alarm( id, interval, repeat, function )
+static int tmr_alarm(lua_State* L){
+ tmr_register(L);
+ return tmr_start(L);
}
-void alarm_timer_cb5(void *arg){
- if( !arg )
- return;
- alarm_timer_common((lua_State*)arg, 5);
+// Lua: tmr.stop( id )
+static int tmr_stop(lua_State* L){
+ uint8_t id = luaL_checkinteger(L, 1);
+ MOD_CHECK_ID(tmr,id);
+ timer_t tmr = &alarm_timers[id];
+ //we return false if the timer is idle (of not registered)
+ if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF){
+ tmr->mode |= TIMER_IDLE_FLAG;
+ ets_timer_disarm(&tmr->os);
+ lua_pushboolean(L, 1);
+ }else{
+ lua_pushboolean(L, 0);
+ }
+ return 1;
}
-void alarm_timer_cb6(void *arg){
- if( !arg )
- return;
- alarm_timer_common((lua_State*)arg, 6);
+// Lua: tmr.unregister( id )
+static int tmr_unregister(lua_State* L){
+ uint8_t id = luaL_checkinteger(L, 1);
+ MOD_CHECK_ID(tmr,id);
+ timer_t tmr = &alarm_timers[id];
+ if(!(tmr->mode & TIMER_IDLE_FLAG) && tmr->mode != TIMER_MODE_OFF)
+ ets_timer_disarm(&tmr->os);
+ if(tmr->lua_ref != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, tmr->lua_ref);
+ tmr->lua_ref = LUA_NOREF;
+ tmr->mode = TIMER_MODE_OFF;
+ return 0;
}
-typedef void (*alarm_timer_callback)(void *arg);
-static alarm_timer_callback alarm_timer_cb[NUM_TMR] = {alarm_timer_cb0,alarm_timer_cb1,alarm_timer_cb2,alarm_timer_cb3,alarm_timer_cb4,alarm_timer_cb5,alarm_timer_cb6};
-
-// Lua: delay( us )
-static int tmr_delay( lua_State* L )
-{
- s32 us;
- us = luaL_checkinteger( L, 1 );
- if ( us <= 0 )
- return luaL_error( L, "wrong arg range" );
- if(us<1000000)
- {
- os_delay_us( us );
- WRITE_PERI_REG(0x60000914, 0x73);
- return 0;
- }
- unsigned sec = (unsigned)us / 1000000;
- unsigned remain = (unsigned)us % 1000000;
- int i = 0;
- for(i=0;i0)
- os_delay_us( remain );
- return 0;
+// Lua: tmr.interval( id, interval )
+static int tmr_interval(lua_State* L){
+ uint8_t id = luaL_checkinteger(L, 1);
+ MOD_CHECK_ID(tmr,id);
+ timer_t tmr = &alarm_timers[id];
+ sint32_t interval = luaL_checkinteger(L, 2);
+ if(interval <= 0)
+ return luaL_error(L, "wrong arg range");
+ if(tmr->mode != TIMER_MODE_OFF){
+ tmr->interval = interval;
+ if(!(tmr->mode&TIMER_IDLE_FLAG)){
+ ets_timer_disarm(&tmr->os);
+ ets_timer_arm_new(&tmr->os, tmr->interval, tmr->mode==TIMER_MODE_AUTO, 1);
+ }
+ }
+ return 0;
}
-// Lua: now() , return system timer in us
-static int tmr_now( lua_State* L )
-{
- unsigned now = 0x7FFFFFFF & system_get_time();
- lua_pushinteger( L, now );
- return 1;
+// Lua: tmr.state( id )
+static int tmr_state(lua_State* L){
+ uint8_t id = luaL_checkinteger(L, 1);
+ MOD_CHECK_ID(tmr,id);
+ timer_t tmr = &alarm_timers[id];
+ if(tmr->mode == TIMER_MODE_OFF){
+ lua_pushnil(L);
+ return 1;
+ }
+ lua_pushboolean(L, (tmr->mode&TIMER_IDLE_FLAG)==0);
+ lua_pushinteger(L, tmr->mode&(~TIMER_IDLE_FLAG));
+ return 2;
}
-// Lua: alarm( id, interval, repeat, function )
-static int tmr_alarm( lua_State* L )
-{
- s32 interval;
- unsigned repeat = 0;
- int stack = 1;
-
- unsigned id = luaL_checkinteger( L, stack );
- stack++;
- MOD_CHECK_ID( tmr, id );
-
- interval = luaL_checkinteger( L, stack );
- stack++;
- if ( interval <= 0 )
- return luaL_error( L, "wrong arg range" );
-
- if ( lua_isnumber(L, stack) ){
- repeat = lua_tointeger(L, stack);
- stack++;
- if ( repeat != 1 && repeat != 0 )
- return luaL_error( L, "wrong arg type" );
- alarm_timer_repeat[id]=repeat;
- }
-
- // luaL_checkanyfunction(L, stack);
- if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){
- lua_pushvalue(L, stack); // copy argument (func) to the top of stack
- if(alarm_timer_cb_ref[id] != LUA_NOREF)
- luaL_unref(L, LUA_REGISTRYINDEX, alarm_timer_cb_ref[id]);
- alarm_timer_cb_ref[id] = luaL_ref(L, LUA_REGISTRYINDEX);
- }
-
- os_timer_disarm(&(alarm_timer[id]));
- os_timer_setfn(&(alarm_timer[id]), (os_timer_func_t *)(alarm_timer_cb[id]), L);
- os_timer_arm(&(alarm_timer[id]), interval, repeat);
- return 0;
-}
-
-// Lua: stop( id )
-static int tmr_stop( lua_State* L )
-{
- unsigned id = luaL_checkinteger( L, 1 );
- MOD_CHECK_ID( tmr, id );
-
- os_timer_disarm(&(alarm_timer[id]));
- if(alarm_timer_cb_ref[id] != LUA_NOREF)
- luaL_unref(L, LUA_REGISTRYINDEX, alarm_timer_cb_ref[id]);
-
- return 0;
-}
+/*I left the led comments 'couse I don't know
+why they are here*/
// extern void update_key_led();
-// Lua: wdclr()
-static int tmr_wdclr( lua_State* L )
-{
- WRITE_PERI_REG(0x60000914, 0x73);
- // update_key_led();
- return 0;
+// Lua: tmr.wdclr()
+static int tmr_wdclr( lua_State* L ){
+ WRITE_PERI_REG(0x60000914, 0x73);
+ // update_key_led();
+ return 0;
}
-static os_timer_t rtc_timer_updator;
-static uint32_t cur_count = 0;
-static uint32_t rtc_10ms = 0;
-void rtc_timer_update_cb(void *arg){
- uint32_t t = (uint32_t)system_get_rtc_time();
- uint32_t delta = 0;
- if(t>=cur_count){
- delta = t-cur_count;
- }else{
- delta = 0xFFFFFFF - cur_count + t + 1;
- }
- // uint64_t delta = (t>=cur_count)?(t - cur_count):(0x100000000 + t - cur_count);
- // NODE_ERR("%x\n",t);
- cur_count = t;
- uint32_t c = system_rtc_clock_cali_proc();
- uint32_t itg = c >> 12; // ~=5
- uint32_t dec = c & 0xFFF; // ~=2ff
- rtc_10ms += (delta*itg + ((delta*dec)>>12)) / 10000;
- // TODO: store rtc_10ms to rtc memory.
+//system_rtc_clock_cali_proc() returns
+//a fixed point value (12 bit fraction part)
+//it tells how many rtc clock ticks represent 1us.
+//the high 64 bits of the uint64_t multiplication
+//are unnedded (I did the math)
+static uint32_t rtc2sec(uint64_t rtc){
+ uint64_t aku = system_rtc_clock_cali_proc();
+ aku *= rtc;
+ return (aku>>12)/1000000;
}
-// Lua: time() , return rtc time in second
-static int tmr_time( lua_State* L )
-{
- uint32_t local = rtc_10ms;
- lua_pushinteger( L, ((uint32_t)(local/100)) & 0x7FFFFFFF );
- return 1;
+
+//the following function workes, I just wrote it and didn't use it.
+/*static uint64_t sec2rtc(uint32_t sec){
+ uint64_t aku = (1<<20)/system_rtc_clock_cali_proc();
+ aku *= sec;
+ return (aku>>8)*1000000;
+}*/
+
+inline static void rtc_timer_update(){
+ uint32_t current = system_get_rtc_time();
+ if(rtc_time.part[0] > current) //overflow check
+ rtc_time.part[1]++;
+ rtc_time.part[0] = current;
+}
+
+void rtc_callback(void *arg){
+ rtc_timer_update();
+ if(soft_watchdog > 0){
+ soft_watchdog--;
+ if(soft_watchdog == 0)
+ system_restart();
+ }
+}
+
+// Lua: tmr.time() , return rtc time in second
+static int tmr_time( lua_State* L ){
+ rtc_timer_update();
+ lua_pushinteger(L, rtc2sec(rtc_time.block));
+ return 1;
+}
+
+// Lua: tmr.softwd( value )
+static int tmr_softwd( lua_State* L ){
+ soft_watchdog = luaL_checkinteger(L, 1);
+ return 0;
}
// Module function map
-#define MIN_OPT_LEVEL 2
-#include "lrodefs.h"
-const LUA_REG_TYPE tmr_map[] =
-{
- { LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) },
- { LSTRKEY( "now" ), LFUNCVAL( tmr_now ) },
- { LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) },
- { LSTRKEY( "stop" ), LFUNCVAL( tmr_stop ) },
- { LSTRKEY( "wdclr" ), LFUNCVAL( tmr_wdclr ) },
- { LSTRKEY( "time" ), LFUNCVAL( tmr_time ) },
-#if LUA_OPTIMIZE_MEMORY > 0
+const LUA_REG_TYPE tmr_map[] = {
+ { LSTRKEY( "delay" ), LFUNCVAL( tmr_delay ) },
+ { LSTRKEY( "now" ), LFUNCVAL( tmr_now ) },
+ { LSTRKEY( "wdclr" ), LFUNCVAL( tmr_wdclr ) },
+ { LSTRKEY( "softwd" ), LFUNCVAL( tmr_softwd ) },
+ { LSTRKEY( "time" ), LFUNCVAL( tmr_time ) },
+ { LSTRKEY( "register" ), LFUNCVAL ( tmr_register ) },
+ { LSTRKEY( "alarm" ), LFUNCVAL( tmr_alarm ) },
+ { LSTRKEY( "start" ), LFUNCVAL ( tmr_start ) },
+ { LSTRKEY( "stop" ), LFUNCVAL ( tmr_stop ) },
+ { LSTRKEY( "unregister" ), LFUNCVAL ( tmr_unregister ) },
+ { LSTRKEY( "state" ), LFUNCVAL ( tmr_state ) },
+ { LSTRKEY( "interval" ), LFUNCVAL ( tmr_interval) },
+#if LUA_OPTIMIZE_MEMORY > 0
+ { LSTRKEY( "ALARM_SINGLE" ), LNUMVAL( TIMER_MODE_SINGLE ) },
+ { LSTRKEY( "ALARM_SEMI" ), LNUMVAL( TIMER_MODE_SEMI ) },
+ { LSTRKEY( "ALARM_AUTO" ), LNUMVAL( TIMER_MODE_AUTO ) },
#endif
- { LNILKEY, LNILVAL }
+ { LNILKEY, LNILVAL }
};
-LUALIB_API int luaopen_tmr( lua_State *L )
-{
- int i = 0;
- for(i=0;i 0
- return 0;
-#else // #if LUA_OPTIMIZE_MEMORY > 0
- luaL_register( L, AUXLIB_TMR, tmr_map );
- // Add constants
-
- return 1;
-#endif // #if LUA_OPTIMIZE_MEMORY > 0
+ return 0;
+#else
+ luaL_register( L, AUXLIB_TMR, tmr_map );
+ lua_pushvalue( L, -1 );
+ lua_setmetatable( L, -2 );
+ MOD_REG_NUMBER( L, "ALARM_SINGLE", TIMER_MODE_SINGLE );
+ MOD_REG_NUMBER( L, "ALARM_SEMI", TIMER_MODE_SEMI );
+ MOD_REG_NUMBER( L, "ALARM_AUTO", TIMER_MODE_AUTO );
+ return 1;
+#endif
}
+
diff --git a/app/modules/u8g.c b/app/modules/u8g.c
index 3596ab06..b046635e 100644
--- a/app/modules/u8g.c
+++ b/app/modules/u8g.c
@@ -26,21 +26,6 @@ typedef struct _lu8g_userdata_t lu8g_userdata_t;
#define LU8G (&(lud->u8g))
-// function to read 4-byte aligned from program memory AKA irom0
-u8g_pgm_uint8_t ICACHE_FLASH_ATTR u8g_pgm_read(const u8g_pgm_uint8_t *adr)
-{
- uint32_t iadr = (uint32_t)adr;
- // set up pointer to 4-byte aligned memory location
- uint32_t *ptr = (uint32_t *)(iadr & ~0x3);
-
- // read 4-byte aligned
- uint32_t pgm_data = *ptr;
-
- // return the correct byte within the retrieved 32bit word
- return pgm_data >> ((iadr % 4) * 8);
-}
-
-
// helper function: retrieve and check userdata argument
static lu8g_userdata_t *get_lud( lua_State *L )
{
@@ -889,22 +874,12 @@ uint8_t u8g_com_esp8266_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void
break;
case U8G_COM_MSG_WRITE_SEQ:
- {
- register uint8_t *ptr = arg_ptr;
- while( arg_val > 0 )
- {
- platform_spi_send_recv( 1, *ptr++ );
- arg_val--;
- }
- }
- break;
case U8G_COM_MSG_WRITE_SEQ_P:
{
register uint8_t *ptr = arg_ptr;
while( arg_val > 0 )
{
- platform_spi_send_recv( 1, u8g_pgm_read(ptr) );
- ptr++;
+ platform_spi_send_recv( 1, *ptr++ );
arg_val--;
}
}
@@ -957,6 +932,7 @@ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi
break;
case U8G_COM_MSG_WRITE_SEQ:
+ case U8G_COM_MSG_WRITE_SEQ_P:
//u8g->pin_list[U8G_PI_SET_A0] = 1;
if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 )
return platform_i2c_send_stop( ESP_I2C_ID ), 0;
@@ -973,24 +949,6 @@ uint8_t u8g_com_esp8266_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi
// platform_i2c_send_stop( ESP_I2C_ID );
break;
- case U8G_COM_MSG_WRITE_SEQ_P:
- //u8g->pin_list[U8G_PI_SET_A0] = 1;
- if ( u8g_com_esp8266_ssd_start_sequence(u8g) == 0 )
- return platform_i2c_send_stop( ESP_I2C_ID ), 0;
- {
- register uint8_t *ptr = arg_ptr;
- while( arg_val > 0 )
- {
- // ignore return value -> tolerate missing ACK
- if ( platform_i2c_send_byte( ESP_I2C_ID, u8g_pgm_read(ptr) ) == 0 )
- ; //return 0;
- ptr++;
- arg_val--;
- }
- }
- // platform_i2c_send_stop( ESP_I2C_ID );
- break;
-
case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */
u8g->pin_list[U8G_PI_A0_STATE] = arg_val;
u8g->pin_list[U8G_PI_SET_A0] = 1; /* force a0 to set again */
diff --git a/app/modules/wifi.c b/app/modules/wifi.c
index b796babb..fd700b27 100644
--- a/app/modules/wifi.c
+++ b/app/modules/wifi.c
@@ -17,23 +17,48 @@
static int wifi_smart_succeed = LUA_NOREF;
static uint8 getap_output_format=0;
-
+#if defined( NODE_SMART_OLDSTYLE )
+#else
+static lua_State* smart_L = NULL;
+#endif
static void wifi_smart_succeed_cb(void *arg){
NODE_DBG("wifi_smart_succeed_cb is called.\n");
+
+#if defined( NODE_SMART_OLDSTYLE )
+
if( !arg )
return;
-#if 0
+ if(wifi_smart_succeed == LUA_NOREF)
+ return;
+
+ lua_State* L = (lua_State *)arg;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, wifi_smart_succeed);
+ lua_call(L, 0, 0);
+
+#else
+
+ if( !arg )
+ return;
+
struct station_config *sta_conf = arg;
wifi_station_set_config(sta_conf);
wifi_station_disconnect();
wifi_station_connect();
+
+ if(wifi_smart_succeed != LUA_NOREF)
+ {
+ lua_rawgeti(smart_L, LUA_REGISTRYINDEX, wifi_smart_succeed);
+
+ lua_pushstring(smart_L, sta_conf->ssid);
+ lua_pushstring(smart_L, sta_conf->password);
+ lua_call(smart_L, 2, 0);
+
+ luaL_unref(smart_L, LUA_REGISTRYINDEX, wifi_smart_succeed);
+ wifi_smart_succeed = LUA_NOREF;
+ }
smartconfig_stop();
-#endif
- if(wifi_smart_succeed == LUA_NOREF)
- return;
- lua_State* L = (lua_State *)arg;
- lua_rawgeti(L, LUA_REGISTRYINDEX, wifi_smart_succeed);
- lua_call(L, 0, 0);
+
+#endif // defined( NODE_SMART_OLDSTYLE )
}
static int wifi_scan_succeed = LUA_NOREF;
@@ -107,6 +132,9 @@ static void wifi_scan_done(void *arg, STATUS status)
// Lua: smart(type, function succeed_cb)
static int wifi_start_smart( lua_State* L )
{
+
+#if defined( NODE_SMART_OLDSTYLE )
+
unsigned channel;
int stack = 1;
@@ -133,15 +161,49 @@ static int wifi_start_smart( lua_State* L )
}else{
smart_begin(channel, (smart_succeed )wifi_smart_succeed_cb, L);
}
- // smartconfig_start(0, wifi_smart_succeed_cb);
+
+#else
+
+ if(wifi_get_opmode() != STATION_MODE)
+ {
+ return luaL_error( L, "Smart link only in STATION mode" );
+ }
+ uint8_t smart_type = 0;
+ int stack = 1;
+ smart_L = L;
+ if ( lua_isnumber(L, stack) )
+ {
+ smart_type = lua_tointeger(L, stack);
+ stack++;
+ }
+
+ if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION)
+ {
+ lua_pushvalue(L, stack); // copy argument (func) to the top of stack
+ if(wifi_smart_succeed != LUA_NOREF)
+ luaL_unref(L, LUA_REGISTRYINDEX, wifi_smart_succeed);
+ wifi_smart_succeed = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+
+ if ( smart_type > 1 )
+ return luaL_error( L, "wrong arg range" );
+
+ smartconfig_start(smart_type, wifi_smart_succeed_cb);
+
+#endif // defined( NODE_SMART_OLDSTYLE )
+
return 0;
}
// Lua: exit_smart()
static int wifi_exit_smart( lua_State* L )
{
+#if defined( NODE_SMART_OLDSTYLE )
smart_end();
- // smartconfig_stop();
+#else
+ smartconfig_stop();
+#endif // defined( NODE_SMART_OLDSTYLE )
+
if(wifi_smart_succeed != LUA_NOREF)
luaL_unref(L, LUA_REGISTRYINDEX, wifi_smart_succeed);
wifi_smart_succeed = LUA_NOREF;
@@ -815,6 +877,19 @@ static int wifi_ap_getbroadcast( lua_State* L ){
return wifi_getbroadcast(L, SOFTAP_IF);
}
+// Lua: wifi.ap.getconfig()
+static int wifi_ap_getconfig( lua_State* L )
+{
+ struct softap_config config;
+ wifi_softap_get_config(&config);
+ lua_pushstring( L, config.ssid );
+ if(config.authmode = AUTH_OPEN)
+ lua_pushnil(L);
+ else
+ lua_pushstring( L, config.password );
+ return 2;
+}
+
// Lua: wifi.ap.config(table)
static int wifi_ap_config( lua_State* L )
{
@@ -1030,6 +1105,7 @@ static const LUA_REG_TYPE wifi_station_map[] =
{ LSTRKEY( "connect" ), LFUNCVAL ( wifi_station_connect4lua ) },
{ LSTRKEY( "disconnect" ), LFUNCVAL ( wifi_station_disconnect4lua ) },
{ LSTRKEY( "autoconnect" ), LFUNCVAL ( wifi_station_setauto ) },
+ { LSTRKEY( "getconfig" ), LFUNCVAL( wifi_ap_getconfig ) },
{ LSTRKEY( "getip" ), LFUNCVAL ( wifi_station_getip ) },
{ LSTRKEY( "setip" ), LFUNCVAL ( wifi_station_setip ) },
{ LSTRKEY( "getbroadcast" ), LFUNCVAL ( wifi_station_getbroadcast) },
diff --git a/app/modules/ws2801.c b/app/modules/ws2801.c
new file mode 100644
index 00000000..640eb066
--- /dev/null
+++ b/app/modules/ws2801.c
@@ -0,0 +1,140 @@
+#include "lualib.h"
+#include "lauxlib.h"
+#include "platform.h"
+#include "auxmods.h"
+#include "lrotable.h"
+#include "c_stdlib.h"
+#include "c_string.h"
+
+/**
+ * Code is based on https://github.com/CHERTS/esp8266-devkit/blob/master/Espressif/examples/EspLightNode/user/ws2801.c
+ * and provides a similar api as the ws2812 module.
+ * The current implementation allows the caller to use
+ * any combination of GPIO0, GPIO2, GPIO4, GPIO5 as clock and data.
+ */
+
+#define PIN_CLK_DEFAULT 0
+#define PIN_DATA_DEFAULT 2
+
+static uint32_t ws2801_bit_clk;
+static uint32_t ws2801_bit_data;
+
+static void ws2801_byte(uint8_t n) {
+ uint8_t bitmask;
+ for (bitmask = 0x80; bitmask !=0 ; bitmask >>= 1) {
+ if (n & bitmask) {
+ GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, ws2801_bit_data);
+ } else {
+ GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, ws2801_bit_data);
+ }
+ GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, ws2801_bit_clk);
+ GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, ws2801_bit_clk);
+ }
+}
+
+static void ws2801_color(uint8_t r, uint8_t g, uint8_t b) {
+ ws2801_byte(r);
+ ws2801_byte(g);
+ ws2801_byte(b);
+}
+
+static void ws2801_strip(uint8_t const * data, uint16_t len) {
+ while (len--) {
+ ws2801_byte(*(data++));
+ }
+ GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, ws2801_bit_data);
+}
+
+static void enable_pin_mux(pin) {
+ // The API only supports setting PERIPHS_IO_MUX on GPIO 0, 2, 4, 5
+ switch (pin) {
+ case 0:
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0);
+ break;
+ case 2:
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
+ break;
+ case 4:
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U, FUNC_GPIO4);
+ break;
+ case 5:
+ PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U, FUNC_GPIO5);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Lua: ws2801.init(pin_clk, pin_data)
+ * Sets up the GPIO pins
+ *
+ * ws2801.init(0, 2) uses GPIO0 as clock and GPIO2 as data.
+ * This is the default behavior.
+ */
+static int ICACHE_FLASH_ATTR ws2801_init_lua(lua_State* L) {
+ uint32_t pin_clk;
+ uint32_t pin_data;
+ uint32_t func_gpio_clk;
+ uint32_t func_gpio_data;
+
+ if (!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) {
+ // Use default pins if the input is omitted
+ pin_clk = PIN_CLK_DEFAULT;
+ pin_data = PIN_DATA_DEFAULT;
+ } else {
+ pin_clk = luaL_checkinteger(L, 1);
+ pin_data = luaL_checkinteger(L, 2);
+ }
+
+ ws2801_bit_clk = 1 << pin_clk;
+ ws2801_bit_data = 1 << pin_data;
+
+ os_delay_us(10);
+
+ //Set GPIO pins to output mode
+ enable_pin_mux(pin_clk);
+ enable_pin_mux(pin_data);
+
+ //Set both GPIOs low low
+ gpio_output_set(0, ws2801_bit_clk | ws2801_bit_data, ws2801_bit_clk | ws2801_bit_data, 0);
+
+ os_delay_us(10);
+}
+
+/* Lua: ws2801.write(pin, "string")
+ * Byte triples in the string are interpreted as R G B values.
+ * This function does not corrupt your buffer.
+ *
+ * ws2801.write(string.char(255, 0, 0)) sets the first LED red.
+ * ws2801.write(string.char(0, 0, 255):rep(10)) sets ten LEDs blue.
+ * ws2801.write(string.char(0, 255, 0, 255, 255, 255)) first LED green, second LED white.
+ */
+static int ICACHE_FLASH_ATTR ws2801_writergb(lua_State* L) {
+ size_t length;
+ const char *buffer = luaL_checklstring(L, 1, &length);
+
+ os_delay_us(10);
+
+ os_intr_lock();
+
+ ws2801_strip(buffer, length);
+
+ os_intr_unlock();
+
+ return 0;
+}
+
+#define MIN_OPT_LEVEL 2
+#include "lrodefs.h"
+const LUA_REG_TYPE ws2801_map[] =
+{
+ { LSTRKEY( "write" ), LFUNCVAL( ws2801_writergb )},
+ { LSTRKEY( "init" ), LFUNCVAL( ws2801_init_lua )},
+ { LNILKEY, LNILVAL}
+};
+
+LUALIB_API int luaopen_ws2801(lua_State *L) {
+ LREGISTER(L, "ws2801", ws2801_map);
+ return 1;
+}
+
diff --git a/app/u8glib/u8g.h b/app/u8glib/u8g.h
index 7aa2845a..e62fb024 100644
--- a/app/u8glib/u8g.h
+++ b/app/u8glib/u8g.h
@@ -88,7 +88,7 @@ extern "C" {
# define U8G_FONT_SECTION(name) U8G_SECTION(".progmem." name)
# endif
# if defined(__XTENSA__)
-# define U8G_FONT_SECTION(name) U8G_SECTION(".u8g_progmem." name)
+# define U8G_FONT_SECTION(name)
# endif
#else
# define U8G_NOINLINE
@@ -116,10 +116,10 @@ typedef uint8_t u8g_fntpgm_uint8_t;
#elif defined(__XTENSA__)
# define U8G_PROGMEM
-# define PROGMEM U8G_SECTION(".u8g_progmem.data")
+# define PROGMEM
typedef uint8_t u8g_pgm_uint8_t;
typedef uint8_t u8g_fntpgm_uint8_t;
- u8g_pgm_uint8_t u8g_pgm_read(const u8g_pgm_uint8_t *adr);
+# define u8g_pgm_read(adr) (*(const u8g_pgm_uint8_t *)(adr))
# define U8G_PSTR(s) ((u8g_pgm_uint8_t *)(s))
#else
diff --git a/app/user/user_exceptions.c b/app/user/user_exceptions.c
new file mode 100644
index 00000000..fb26e76a
--- /dev/null
+++ b/app/user/user_exceptions.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2015 Dius Computing Pty Ltd. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ * - Neither the name of the copyright holders nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @author Johny Mattsson
+ */
+
+#include "user_exceptions.h"
+
+#define LOAD_MASK 0x00f00fu
+#define L8UI_MATCH 0x000002u
+#define L16UI_MATCH 0x001002u
+#define L16SI_MATCH 0x009002u
+
+
+void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause)
+{
+ /* If this is not EXCCAUSE_LOAD_STORE_ERROR you're doing it wrong! */
+ (void)cause;
+
+ uint32_t epc1 = ef->epc;
+ uint32_t excvaddr;
+ uint32_t insn;
+ asm (
+ "rsr %0, EXCVADDR;" /* read out the faulting address */
+ "movi a4, ~3;" /* prepare a mask for the EPC */
+ "and a4, a4, %2;" /* apply mask for 32bit aligned base */
+ "l32i a5, a4, 0;" /* load part 1 */
+ "l32i a6, a4, 4;" /* load part 2 */
+ "ssa8l %2;" /* set up shift register for src op */
+ "src %1, a6, a5;" /* right shift to get faulting instruction */
+ :"=r"(excvaddr), "=r"(insn)
+ :"r"(epc1)
+ :"a4", "a5", "a6"
+ );
+
+ uint32_t valmask = 0;
+ uint32_t what = insn & LOAD_MASK;
+
+ if (what == L8UI_MATCH)
+ valmask = 0xffu;
+ else if (what == L16UI_MATCH || what == L16SI_MATCH)
+ valmask = 0xffffu;
+ else
+ {
+die:
+ /* Turns out we couldn't fix this, trigger a system break instead
+ * and hang if the break doesn't get handled. This is effectively
+ * what would happen if the default handler was installed. */
+ asm ("break 1, 1");
+ while (1) {}
+ }
+
+ /* Load, shift and mask down to correct size */
+ uint32_t val = (*(uint32_t *)(excvaddr & ~0x3));
+ val >>= (excvaddr & 0x3) * 8;
+ val &= valmask;
+
+ /* Sign-extend for L16SI, if applicable */
+ if (what == L16SI_MATCH && (val & 0x8000))
+ val |= 0xffff0000;
+
+ int regno = (insn & 0x0000f0u) >> 4;
+ if (regno == 1)
+ goto die; /* we can't support loading into a1, just die */
+ else if (regno != 0)
+ --regno; /* account for skipped a1 in exception_frame */
+
+ ef->a_reg[regno] = val; /* carry out the load */
+ ef->epc += 3; /* resume at following instruction */
+}
+
+
+/**
+ * The SDK's user_main function installs a debugging handler regardless
+ * of whether there's a proper handler installed for EXCCAUSE_LOAD_STORE_ERROR,
+ * which of course breaks everything if we allow that to go through. As such,
+ * we use the linker to wrap that call and stop the SDK from shooting itself in
+ * its proverbial foot.
+ */
+exception_handler_fn
+__wrap__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn)
+{
+ if (cause != EXCCAUSE_LOAD_STORE_ERROR)
+ __real__xtos_set_exception_handler (cause, fn);
+}
diff --git a/app/user/user_exceptions.h b/app/user/user_exceptions.h
new file mode 100644
index 00000000..d2cf5166
--- /dev/null
+++ b/app/user/user_exceptions.h
@@ -0,0 +1,5 @@
+#include "c_types.h"
+#include "rom.h"
+#include
+
+void load_non_32_wide_handler (struct exception_frame *ef, uint32_t cause) TEXT_SECTION_ATTR;
diff --git a/app/user/user_main.c b/app/user/user_main.c
index 88938da8..c24e0f00 100644
--- a/app/user/user_main.c
+++ b/app/user/user_main.c
@@ -16,6 +16,7 @@
#include "flash_fs.h"
#include "user_interface.h"
+#include "user_exceptions.h"
#include "ets_sys.h"
#include "driver/uart.h"
@@ -25,6 +26,21 @@
#define TASK_QUEUE_LEN 4
os_event_t *taskQueue;
+
+/* Note: the trampoline *must* be explicitly put into the .text segment, since
+ * by the time it is invoked the irom has not yet been mapped. This naturally
+ * also goes for anything the trampoline itself calls.
+ */
+void user_start_trampoline (void) TEXT_SECTION_ATTR;
+void user_start_trampoline (void)
+{
+ __real__xtos_set_exception_handler (
+ EXCCAUSE_LOAD_STORE_ERROR, load_non_32_wide_handler);
+
+ call_user_start ();
+}
+
+
void task_lua(os_event_t *e){
char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL };
NODE_DBG("Task task_lua started.\n");
diff --git a/include/c_types.h b/include/c_types.h
index 55bffd46..2cd6332a 100644
--- a/include/c_types.h
+++ b/include/c_types.h
@@ -84,6 +84,9 @@ typedef enum {
#define ICACHE_FLASH_ATTR
#endif /* ICACHE_FLASH */
+#define TEXT_SECTION_ATTR __attribute__((section(".text")))
+#define RAM_CONST_ATTR __attribute__((section(".text")))
+
#ifndef __cplusplus
typedef unsigned char bool;
#define BOOL bool
diff --git a/ld/eagle.app.v6.ld b/ld/eagle.app.v6.ld
index a36cb31e..273808e2 100644
--- a/ld/eagle.app.v6.ld
+++ b/ld/eagle.app.v6.ld
@@ -5,7 +5,7 @@ MEMORY
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x8000
- irom0_0_seg : org = 0x40210000, len = 0x60000
+ irom0_0_seg : org = 0x40210000, len = 0x80000
}
PHDRS
@@ -19,7 +19,7 @@ PHDRS
/* Default entry point: */
-ENTRY(call_user_start)
+ENTRY(user_start_trampoline)
PROVIDE(_memmap_vecbase_reset = 0x40000000);
/* Various memory-map dependent cache attribute settings: */
_memmap_cacheattr_wb_base = 0x00000110;
@@ -72,8 +72,9 @@ SECTIONS
*(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text)
*(.literal.* .text.*)
- /* put font and progmem data into irom0 */
- *(.u8g_progmem.*)
+ /* Trade some performance for lots of ram. At the time of writing the
+ * available Lua heap went from 18248 to 34704. */
+ *(.rodata*)
_irom0_text_end = ABSOLUTE(.);
_flash_used_end = ABSOLUTE(.);
@@ -124,10 +125,7 @@ SECTIONS
.rodata : ALIGN(4)
{
_rodata_start = ABSOLUTE(.);
- *(.rodata)
- *(.rodata.*)
*(.gnu.linkonce.r.*)
- *(.rodata1)
__XT_EXCEPTION_TABLE__ = ABSOLUTE(.);
*(.xt_except_table)
*(.gcc_except_table)
diff --git a/lib/libsmartconfig.a b/lib/libsmartconfig.a
index 9a8db685..ca796a78 100644
Binary files a/lib/libsmartconfig.a and b/lib/libsmartconfig.a differ
diff --git a/lua_modules/base64/base64_v2.lua b/lua_modules/base64/base64_v2.lua
new file mode 100644
index 00000000..1180f526
--- /dev/null
+++ b/lua_modules/base64/base64_v2.lua
@@ -0,0 +1,36 @@
+--this version actually works
+
+local tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+base64 = {
+ enc = function(data)
+ local l,out = 0,''
+ local m = (3-data:len()%3)%3
+ local d = data..string.rep('\0',m)
+ for i=1,d:len() do
+ l = bit.lshift(l,8)
+ l = l+d:byte(i,i)
+ if i%3==0 then
+ for j=1,4 do
+ local a = bit.rshift(l,18)+1
+ out = out..tab:sub(a,a)
+ l = bit.band(bit.lshift(l,6),0xFFFFFF)
+ end
+ end
+ end
+ return out:sub(1,-1-m)..string.rep('=',m)
+ end,
+ dec = function(data)
+ local a,b = data:gsub('=','A')
+ local out = ''
+ local l = 0
+ for i=1,a:len() do
+ l=l+tab:find(a:sub(i,i))-1
+ if i%4==0 then
+ out=out..string.char(bit.rshift(l,16),bit.band(bit.rshift(l,8),255),bit.band(l,255))
+ l=0
+ end
+ l=bit.lshift(l,6)
+ end
+ return out:sub(1,-b-1)
+ end
+}
diff --git a/tools/esp-open-sdk.tar.gz b/tools/esp-open-sdk.tar.gz
new file mode 100644
index 00000000..87af5572
Binary files /dev/null and b/tools/esp-open-sdk.tar.gz differ
diff --git a/tools/esptool.py b/tools/esptool.py
index 88a4b4ed..944a59b7 100755
--- a/tools/esptool.py
+++ b/tools/esptool.py
@@ -128,38 +128,32 @@ class ESPROM:
def connect(self):
print 'Connecting...'
- # RTS = CH_PD (i.e reset)
- # DTR = GPIO0
- # self._port.setRTS(True)
- # self._port.setDTR(True)
- # self._port.setRTS(False)
- # time.sleep(0.1)
- # self._port.setDTR(False)
+ for _ in xrange(4):
+ # issue reset-to-bootloader:
+ # RTS = either CH_PD or nRESET (both active low = chip in reset)
+ # DTR = GPIO0 (active low = boot to flasher)
+ self._port.setDTR(False)
+ self._port.setRTS(True)
+ time.sleep(0.05)
+ self._port.setDTR(True)
+ self._port.setRTS(False)
+ time.sleep(0.05)
+ self._port.setDTR(False)
- # NodeMCU devkit
- self._port.setRTS(True)
- self._port.setDTR(True)
- time.sleep(0.1)
- self._port.setRTS(False)
- self._port.setDTR(False)
- time.sleep(0.1)
- self._port.setRTS(True)
- time.sleep(0.1)
- self._port.setDTR(True)
- self._port.setRTS(False)
- time.sleep(0.3)
- self._port.setDTR(True)
-
- self._port.timeout = 0.5
- for i in xrange(10):
- try:
- self._port.flushInput()
- self._port.flushOutput()
- self.sync()
- self._port.timeout = 5
- return
- except:
- time.sleep(0.1)
+ self._port.timeout = 0.3 # worst-case latency timer should be 255ms (probably <20ms)
+ for _ in xrange(4):
+ try:
+ self._port.flushInput()
+ self._port.flushOutput()
+ self.sync()
+ self._port.timeout = 5
+ return
+ except:
+ time.sleep(0.05)
+ # this is a workaround for the CH340 serial driver on current versions of Linux,
+ # which seems to sometimes set the serial port up with wrong parameters
+ self._port.close()
+ self._port.open()
raise Exception('Failed to connect')
""" Read memory address in target """
@@ -268,6 +262,15 @@ class ESPROM:
return data
+ """ Abuse the loader protocol to force flash to be left in write mode """
+ def flash_unlock_dio(self):
+ # Enable flash write mode
+ self.flash_begin(0, 0)
+ # Reset the chip rather than call flash_finish(), which would have
+ # write protected the chip again (why oh why does it do that?!)
+ self.mem_begin(0,0,0,0x40100000)
+ self.mem_finish(0x40000080)
+
""" Perform a chip erase of SPI flash """
def flash_erase(self):
# Trick ROM to initialize SFlash
@@ -360,6 +363,20 @@ class ELFFile:
self._fetch_symbols()
return self.symbols[sym]
+ def get_entry_point(self):
+ tool_readelf = "xtensa-lx106-elf-readelf"
+ if os.getenv('XTENSA_CORE')=='lx106':
+ tool_objcopy = "xt-readelf"
+ try:
+ proc = subprocess.Popen([tool_readelf, "-h", self.name], stdout=subprocess.PIPE)
+ except OSError:
+ print "Error calling "+tool_nm+", do you have Xtensa toolchain in PATH?"
+ sys.exit(1)
+ for l in proc.stdout:
+ fields = l.strip().split()
+ if fields[0] == "Entry":
+ return int(fields[3], 0);
+
def load_section(self, section):
tool_objcopy = "xtensa-lx106-elf-objcopy"
if os.getenv('XTENSA_CORE')=='lx106':
@@ -552,7 +569,10 @@ if __name__ == '__main__':
seq += 1
print
print '\nLeaving...'
- esp.flash_finish(False)
+ if args.flash_mode == 'dio':
+ esp.flash_unlock_dio()
+ else:
+ esp.flash_finish(False)
elif args.operation == 'run':
esp.run()
@@ -586,7 +606,7 @@ if __name__ == '__main__':
args.output = args.input + '-'
e = ELFFile(args.input)
image = ESPFirmwareImage()
- image.entrypoint = e.get_symbol_addr("call_user_start")
+ image.entrypoint = e.get_entry_point()
for section, start in ((".text", "_text_start"), (".data", "_data_start"), (".rodata", "_rodata_start")):
data = e.load_section(section)
image.add_segment(e.get_symbol_addr(start), data)