As mentioned in my prior post, if you have a bug in your init.lua file such that it gets hung in a loop and/or reboots inifinitely, then the only way to correct the situation is to reflash the ESP8266 with nodeMCU. Not something that I want to do on even an occasional basis.
The typical way I’m seeing to get around this problem is to delay the start of your code. During this delay you could delete or rename init.lua.
For my test, I have a file called test.lua that contains:
This little snippet represents my ‘production’ program. This is the program that potentially could have an infinite loop or other bug that prevents regaining control of the ESP8266.
Pausing Before Executing ‘Production’ Program
To allow myself the ability to regain control of the ESP8266, I will wait 5 seconds after the start of the ESP8266 before starting the ‘test.lua’ program file.
This is done in init.lua this manner:
function startup() print('in startup') dofile('test.lua') end tmr.alarm(0,5000,0,startup)
First is a function, called startup, that will run the actual ‘production’ program file from flash.
Next, I use tmr.alarm to execute the startup function 5 seconds after the ESP8266 boots.
Now when I reboot the ESP8266:
NodeMCU 0.9.5 build 20150318 powered by Lua 5.1.4 > Communication with MCU... Got answer! AutoDetect firmware... NodeMCU firmware detected. =node.heap() 20704 > ** 5 second pause here** in startup in test.lua
You can see that I have 5 seconds from the time the prompt is printed until the startup function is invoked. That gives me 5 seconds to interrupt the process.
The conventional way, that I’ve seen so far, to interrupt the process is to delete / rename init.lua. The problem with that, is I guarantee I’m not going to be able to type file.remove(‘init.lua’) fast enough under pressure.
So to use this method of interruption, I create a code snippet button that will delete ‘init.old’ and then rename ‘init.lua’ to ‘init.old’:
Now, I reboot the ESP8266, and as soon as I get a prompt, I press the ‘rename init.lua’ button:
NodeMCU 0.9.5 build 20150318 powered by Lua 5.1.4 > Communication with MCU... Got answer! AutoDetect firmware... NodeMCU firmware detected. =node.heap() 20704 > file.remove('init.old') > file.rename('init.lua','init.old') > in startup in test.lua
NOTE that this DID NOT stop startup from running. However, the next time the ESP8266 is restarted, init.lua will be missing and you will be able to regain control of the ESP8266.
Overriding With a Boolean Variable
This procedure works fine, but I really don’t want to be moving init.lua around like this. It opens the possibility of accidentally erasing it. Murphy’s Law applies to nearly everything I do.
So rather than moving the init.lua file during the 5 second wait, I’d rather just override a boolean variable.
To do this, I create the variable abort, initialized it to false, and test for it in the startup function:
function startup() if abort == true then print('startup aborted') return end print('in startup') dofile('test.lua') end abort = false tmr.alarm(0,5000,0,startup)
Now, when I want to abort the startup process, I have a code snippet I can execute at the first prompt which will abort the startup without messing with init.lua. PLUS, I don’t have to reboot the ESP8266 to regain control:
NodeMCU 0.9.5 build 20150318 powered by Lua 5.1.4 > Communication with MCU... Got answer! AutoDetect firmware... NodeMCU firmware detected. =node.heap() 21032 > abort=true > startup aborted
Interrupting init.lua with the Return Key During Boot
I’m pretty happy with the above solution, but in my production Arduino Code I usually have some type of boot override that occurs by pressing the Return key during boot (typically to let me get in and change program constants). Can the same thing be done with the ESP8266 / nodeMCU?
Here is some code that does just that;
function abortInit() -- initailize abort boolean flag abort = false print('Press ENTER to abort startup') -- if <CR> is pressed, call abortTest uart.on('data', '\r', abortTest, 0) -- start timer to execute startup function in 5 seconds tmr.alarm(0,5000,0,startup) end function abortTest(data) -- user requested abort abort = true -- turns off uart scanning uart.on('data') end function startup() uart.on('data') -- if user requested abort, exit if abort == true then print('startup aborted') return end -- otherwise, start up print('in startup') dofile('test.lua') end tmr.alarm(0,1000,0,abortInit) -- call abortInit after 1s
I think that is pretty self evident. We start abortInit after 1 sec (giving the ESP8266 a bit of time to get going). abortInit sets up the uart to scan for a RETURN and then sets up the startup function to start in 5 seconds.
If the uart sees a RETURN it sets the abort flag to true.
Once the startup function is called, it clears the uart from further scanning, and tests the abort flag to determine if the user program should be called.
Here is what the output looks like WITHOUT aborting:
Communication with MCU... Got answer! AutoDetect firmware... Can't autodetect firmware, because proper answer not received. powered by Lua 5.1.4 > > Press ENTER to abort startup in startup in test.lua
and here it is when I press RETURN after the Press ENTER to abort startup message is displayed:
NodeMCU 0.9.5 build 20150318 powered by Lua 5.1.4 > Communication with MCU... Got answer! AutoDetect firmware... NodeMCU firmware detected. =node.heap() 19280 > Press ENTER to abort startup > startup aborted