When using the ESP8266 chip with the original firmware, AT based command set, to perform operations, implies that something external, like an Arduino or RPi (Raspberry Pi), must be used to drive/command the chip through the serial port. This external device sends out AT commands and waits for responses.
While for some operations/applications, using the AT commands might just work when using the Software Serial library from Arduino. In another words, using a software based serial port to communicate might work. The issue is that even at a slow rate of 9600 baud’s, Arduino has some trouble to follow the esp8266 output, due to the lack of flow control.
For example listing the available access points (AT+CWLAP) or even receiving simple web page from the internet, most of the time the Software Serial port only receives incomplete messages or garbled information. This can be worked around by increasing the software serial port buffer size, but is not a solid solution.
In fact without using the hardware port on the Arduino to connect to the ESP8266, I have doubts that some useful work can be done if we simultaneous also want to use the hardware port for debug ( connecting to the computer) and the software serial port to connect to the esp8266. We must use the hardware port for connecting to the esp8266 and use the software serial library for debugging purposes which brings other issues. This situation only arises on the Arduino Uno or Mini where only one hardware based serial port is available. The Arduino Mega has 4 hardware serial ports, so it doesn’t have this issue.
So why all about this ranting regarding Arduino, serial ports and the esp8266?
The fact is that the esp8266 is by itself a microprocessor and so it is able to do some processing on it’s own. So it’s is possible to only send or receive the already processed information needed from/for Arduino, without the Arduino being the processor that is controlling and defining the behaviour of the esp8266 chip like connecting to WIFI and so on. The AT command set is fine for some applications, but is so 80’s … 🙂
One of the solutions available is to replace the original based AT command set firmware for the ESP8266 with the NodeMcu firmware, that’s now open sourced, that allows to add some intelligence to the esp8266 chip, namely connecting, receiving, sending and waiting for data (server mode) using the Lua language running on the ESP8266 chip itself and not on an external device.
After flashing and restarting the esp8266 with the NodeMcu firmware, the esp8266 tries to load a file named init.lua.
Several words of warning regarding when developing with this firmware:
– If init.lua file crashes, the NodeMcu restarts, and calls again init.lua that crashes again, that restarts NodeMcu and so on. You get the idea. At initial stages do not develop using the init.lua file. The only way to recover from this crash loop is to flash the firmware again. Do your first developing in a file named testing.lua or init2.lua for example. When ready, just rename it to init.lua.
– NoceMcu is single threaded and the esp8266 has a WatchDog timer. Tight loops will trigger the watchdog timer and restart the firmware. If needed we can we use the tmr.wdclr() which resets the watchdog timer, but then other bad things happen due to the single threaded nature of the firmware, like losing wifi connection, timer triggers not activating, and so on.
A great way to develop is to use the ESPlorer IDE tool. It needs Java JDK 1.8 (Edit: It seems not anymore. JDK 7 might be fine), so make sure that version is installed in your Operation System. Then we can just do /opt/jre1.8/bin/java -jar ESPlorer.jar to launch the IDE.
Let’s see how in Lua we can connect to WIFI.
The simplest code for init.lua is
-- This is a comment line print("Configuring WIFI....") -- Print something to the serial port if connected wifi.setmode( wifi.STATION ) -- Client/Station mode. Other mode could be Access Point wifi.sta.config( SSID , APPWD) -- Connect to SSID with password APPWD. -- Example: wifi.sta.config( "MYWNET" , "QuantumEncryptedPassword" ) print( "Ip Address: " .. wifi.sta.getip() ) -- The .. is the string concatenate string operator. dofile ( "RunMyCode.lua")
This code snippet will connect to WIFI, if successful print the IP, and then lauch the RunMyCode.lua file.
But what happens if the connection fails, or takes sometime? We may end lauching the RunMyCode.lua file without WIFI connectivity.
So I’ve lost a couple of hours, learning Lua and esp8266 quirks to develop my init.lua file, with a lot of help of the esp8266 forum where there are a lot of Lua code snippets and examples.
Due to the single threaded nature of the NodeMcu firmware is not possible to do this (a tight loop waiting for the wifi to connect):
isConnected = false repeat -- Lets see if we are already connected by getting the IP ipAddr = wifi.sta.getip() if ( ( ipAddr ~= nil ) and ( ipAddr ~= "0.0.0.0" ) )then -- Check if IP is valid isConnected = true end until ( isConnected == true )
This will hang the esp8266 and will kick the watch dog timer, restarting the firmware. Also the only thread running is the the thread executing this repeat/until loop, and so this code snippet will never connect because the background functions that need to be run, so that the connection does happen, never have a chance to run and the isConnected will always be false. The fact is that after the firmware restart, on the lua prompt running the print ( wifi.sta.getip() ) shows that it connected… So the loop is in fact blocking everything else.
isConnected = false repeat -- Lets see if we are already connected by getting the IP ipAddr = wifi.sta.getip() if ( ( ipAddr ~= nil ) and ( ipAddr ~= "0.0.0.0" ) )then -- Check if IP is valid isConnected = true end tmr.wdclr() -- Reset the watch dog timer. until ( isConnected == true )
This is even worse because this code segment explicitly resets the watchdog, and so we might thing that the loop will succeed some time in the future, but it won’t. The only way to leave this is to reset by hardware. It seems that the only way that background functions can work, if we can say that, is to let the script end…
So how do we test the connection and we need to end the script? Well timers are the solution:
tmr.alarm ( Alarm_number , Number of mS , Repeat , function )
So this: tmr.alarm ( 0 , 2500 , 1 , checkWifi ) will use timer #0 to call periodically each 2.5S the checkWifi function (NOTE the lack of () on the checkWifi parameter (checkWifi is a function)). The Repeat parameter if zero is a one shot call, otherwise it will reset the alarm and trigger it again and again.
So we let the script end and periodically check if we have Wifi. If we have connectivity then we can lauch our code. And so this is my init.lua code:
-- Constants SSID = "MYWIWIF" APPWD = "QuantumPassword" CMDFILE = "ping.lua" -- File that is executed after connection -- Some control variables wifiTrys = 0 -- Counter of trys to connect to wifi NUMWIFITRYS = 200 -- Maximum number of WIFI Testings while waiting for connection -- Change the code of this function that it calls your code. function launch() print("Connected to WIFI!") print("IP Address: " .. wifi.sta.getip()) -- Call our command file every minute. tmr.alarm(0, 60000, 1, function() dofile(CMDFILE) end ) end function checkWIFI() if ( wifiTrys > NUMWIFITRYS ) then print("Sorry. Not able to connect") else ipAddr = wifi.sta.getip() if ( ( ipAddr ~= nil ) and ( ipAddr ~= "0.0.0.0" ) )then -- lauch() -- Cannot call directly the function from here the timer... NodeMcu crashes... tmr.alarm( 1 , 500 , 0 , launch ) else -- Reset alarm again tmr.alarm( 0 , 2500 , 0 , checkWIFI) print("Checking WIFI..." .. wifiTrys) wifiTrys = wifiTrys + 1 end end end print("-- Starting up! ") -- Lets see if we are already connected by getting the IP ipAddr = wifi.sta.getip() if ( ( ipAddr == nil ) or ( ipAddr == "0.0.0.0" ) ) then -- We aren't connected, so let's connect print("Configuring WIFI....") wifi.setmode( wifi.STATION ) wifi.sta.config( SSID , APPWD) print("Waiting for connection") tmr.alarm( 0 , 2500 , 0 , checkWIFI ) -- Call checkWIFI 2.5S in the future. else -- We are connected, so just run the launch code. launch() end -- Drop through here to let NodeMcu run
So we can see that to program the esp8266 in Lua using the NodeMcu firmware we need to change a bit our programming paradigms…