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)