Author Topic: how to setup a rapid development environment for NodeMCU and Lua scripts  (Read 5953 times)

0 Members and 1 Guest are viewing this topic.

Offline FrankBussTopic starter

  • Supporter
  • ****
  • Posts: 2365
  • Country: de
    • Frank Buss
I bought a NodeMcu Lua CH340G ESP8266 Wireless WIFI Internet Development Board Module TE437. First impression was bad, because it comes with no manual and the examples from http://www.nodemcu.com didn't work. Fortunately I found this page, which explains how to start working with it. I'll post the relevant information from it for my module, and at the end some advanced stuff for rapid development, where you can edit a Lua script on PC, and load it to the module over wifi with just pressing the reset button on it.

I tested the board in Linux. lsusb shows this information:

Code: [Select]
Bus 001 Device 012: ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter

The board is a 0.9 hardware version according to the CNXSoft blog, with some disadvantages compared to the latest 1.0 version (wrong pin distance for a breadboard, fewer GPIOs). I can connect to the board with minicom (note: you have to disable hardware flow control, if enabled. On Windows you can use putty or any other terminal program that supports COM ports) :

Code: [Select]
minicom --device /dev/ttyUSB0 --baudrate 115200

After pressing the reset button (it resets the ESP8266 module only, not the USB virtual COM port chip) it shows this information:

Code: [Select]
Ai-Thinker Technology Co. Ltd.

ready

I guess this is an old version or the AT command version, but no AT commands work. Ok, so I installed the latest pre-compiled firmware:

Quote
wget https://github.com/nodemcu/nodemcu-firmware/releases/download/0.9.6-dev_20150704/nodemcu_float_0.9.6-dev_20150704.bin
git clone https://github.com/themadinventor/esptool.git
cd esptool
sudo python2 ./esptool.py --port /dev/ttyUSB0 write_flash 0x00000 ../nodemcu_float_0.9.6-dev_20150704.bin

(if there is an error "ImportError: No module named serial", then do a "sudo python2 -m pip install pyserial". If you have only Python 2.x installed, you can use "python" instead of "python2", this is just my system, where I have Python 3.x installed, too).

Then I can start minicom again. With 115200 baud it shows some garbage text and "MEM CHECK FAIL!!!" after reset. Looks like like there is still some problem, but it works. Later it switches to 9600 baud. When using 9600 baud, I can read this:

Code: [Select]
NodeMCU 0.9.6 build 20150704  powered by Lua 5.1.4
lua: cannot open init.lua
>

Now I can interactively enter

Code: [Select]
print("Hello Lua")

and it executes the command, and the examples from http://www.nodemcu.com work, too. That's a good start! Maybe the eBay seller just didn't care to flash the right firmware version.

Now let's implement a webserver, which toggles the LED and another GPIO pin. Save this as init.lua (slightly modified script from the CNXSoft blog) :

Code: [Select]
wifi.setmode(wifi.STATION)
wifi.sta.config("ROUTER_ESSID","WIFI_PASSWORD")
print(wifi.sta.getip())
gpio.mode(0, gpio.OUTPUT)
gpio.mode(4, gpio.OUTPUT)
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
    conn:on("receive", function(client,request)
        local buf = "";
        local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP");
        if(method == nil)then
            _, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP");
        end
        local _GET = {}
        if (vars ~= nil)then
            for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
                _GET[k] = v
            end
        end
        buf = buf.."<h1> ESP8266 Web Server</h1>";
        buf = buf.."<p>GPIO0 <a href=\"?pin=ON1\"><button>ON</button></a>&nbsp;<a href=\"?pin=OFF1\"><button>OFF</button></a></p>";
        buf = buf.."<p>GPIO4 / LED <a href=\"?pin=ON2\"><button>ON</button></a>&nbsp;<a href=\"?pin=OFF2\"><button>OFF</button></a></p>";
        local _on,_off = "",""
        if(_GET.pin == "ON1")then
            gpio.write(0, gpio.HIGH);
        elseif(_GET.pin == "OFF1")then
            gpio.write(0, gpio.LOW);
        elseif(_GET.pin == "ON2")then
            gpio.write(4, gpio.HIGH);
        elseif(_GET.pin == "OFF2")then
            gpio.write(4, gpio.LOW);
        end
        client:send(buf);
        client:close();
        collectgarbage();
    end)
end)

Replace ROUTER_ESSID and WIFI_PASSWORD with your router information. You can upload it to the module like this:

Quote
git clone https://github.com/4refr0nt/luatool.git
python luatool/luatool/luatool.py --port /dev/ttyUSB0 --src init.lua --dest init.lua --restart

After flashing, it restarts the module. With minicom you can get the IP address:

Code: [Select]
= wifi.sta.getip()

("=" is a shortcut for "print" in the Lua shell)

GPIO0 is labeled D0 on the board and GPIO4 is D4, and the blue LED on the board. That's one direction of an "internet of things" thing, now the other direction, the thing calling the internet.

What I really like about Lua is the interactive shell in minicom, you can try anything you want without the usual compile/upload/run cycle. For a full API documentation take a look at http://nodemcu.readthedocs.io/en/master/

I have a local Apache server installed on my PC. To get a page from it, I can enter this in minicom (one line at a time, looks like it has some problems when you paste lots of data) :

Code: [Select]
conn = net.createConnection(net.TCP, false)
conn:on("receive", function(conn, pl) print(pl) end)
conn:connect(80, "192.168.11.27")
conn:send("GET /init2.lua HTTP/1.1\r\nHost: localhost\r\n\r\n")

Another nice feature of Lua is that you can execute source code from within Lua:

Code: [Select]
> test1='print("test")'
> test2=loadstring(test1)
> test2()
test

Putting it all together and for faster development, this init.lua script loads the file init2.lua from a server and executes it:

Code: [Select]
wifi.setmode(wifi.STATION)
wifi.sta.config("ROUTER_ESSID","WIFI_PASSWORD")

function executeString(s)
    local fun = loadstring(s)
    fun()
end

function stripHeader(s)
    local pos = string.find(s, "\r\n\r\n")
    return s:sub(pos + 4)
end

function runScript(host, url)
    receivedPage = ""
    conn = net.createConnection(net.TCP, false)
    conn:on("receive", function(conn, data) receivedPage = receivedPage .. data end)
    conn:on("disconnection", function(conn, data) executeString(stripHeader(receivedPage)) end)
    conn:connect(80, host)
    conn:send("GET /" .. url .. " HTTP/1.1\r\nHost: " .. host .. "\r\nConnection: close\r\n\r\n")
end

tmr.alarm(0, 3000, 1, function() tmr.stop(0) runScript("192.168.11.27","init2.lua") end)

The 3 second delay before starting is necessary, because it needs some time after reset to establish the wifi connection. In later releases of the firmware you can use wifi.sta.eventMonReg, but for this you have to build your own version of the firmware, or use the firmware build service here: https://nodemcu-build.com For the the later releases they don't provide a default firmware anymore, because they say there are too many modules which the user can select. Yeah, good idea for beginners :palm:

With this setup, I can just change init2.lua on my PC, then hit the reset button on the module and the new code gets loaded. Once I'm done with developing, I can upload init2.lua to the platform, so that I don't need a local webserver anymore.

An interesting application is to get data from the web and display it with some device, for example a clock. There is no internal clock in the firmware, but there is the rtctime module in later releases, which in combination with sntp.sync could be used to load the time from a NTP time server for initialization and then display it. Unfortunately none of these modules are in the old pre-compiled firmware archive.

A simple solution for the old firmware is to create a PHP page on your server which returns the time. The server can be synchronized with ntpd to a NTP time server. For example save this as time.php on your webserver:

Code: [Select]
<?php 
date_default_timezone_set
("Europe/Berlin"); 
echo 
date('H:i:s'time());
?>


Then save this code for init2.lua:

Code: [Select]
function showTime(s)
    print(s)
end

function trim(s)
  return s:match "^%s*(.-)%s*$"
end

function stripHeader(s)
    local pos = string.find(s, "\r\n\r\n")
    return s:sub(pos + 4)
end

function getTime(host)
    receivedPage = ""
    conn = net.createConnection(net.TCP, false)
    conn:on("receive", function(conn, data) receivedPage = receivedPage .. data end)
    conn:on("disconnection", function(conn, data) showTime(trim(stripHeader(receivedPage))) end)
    conn:connect(80, host)
    conn:send("GET /time.php HTTP/1.1\r\nHost: " .. host .. "\r\nConnection: close\r\n\r\n")
end

tmr.alarm(0, 1000, 1, function() getTime("192.168.11.27") end)

and in minicom you can see the time ticking after a reset:

Code: [Select]
NodeMCU 0.9.6 build 20150704  powered by Lua 5.1.4
> 14:25:48
14:25:49
14:25:50
14:25:51
14:25:52
14:25:53
14:25:54
14:25:55

Of course, instead of the time, the PHP script can return any other information you want to display. Controlling the GPIO pins to show the info with a Nixie tube or any other display in the showTime function is trivial and left as an excercise for the reader.

Conclusion: the firmware looks a bit rough, I wouldn't create mission critical projects with it, and if you bought a module with the wrong firmware, it is not easy for beginners to flash a new firmware and to get started, but once this is done and with my network load script, it is a nice platform for fast prototyping and hobby projects.
« Last Edit: January 29, 2017, 04:22:29 am by FrankBuss »
So Long, and Thanks for All the Fish
Electronics, hiking, retro-computing, electronic music etc.: https://www.youtube.com/c/FrankBussProgrammer
 
The following users thanked this post: thm_w, Richard Crowley

Offline djnz

  • Regular Contributor
  • *
  • Posts: 179
  • Country: 00
 

Offline FrankBussTopic starter

  • Supporter
  • ****
  • Posts: 2365
  • Country: de
    • Frank Buss
I think I'm 80% done with my WiFi ePaper project, which uses this module:

https://hackaday.io/project/20466-wifi-epaper/log/57324-first-working-prototype

Please like the project on Hackaday.io, if you like it, because the project owners get one dollar for parts cost for each like :)

So Long, and Thanks for All the Fish
Electronics, hiking, retro-computing, electronic music etc.: https://www.youtube.com/c/FrankBussProgrammer
 

Offline kripton2035

  • Super Contributor
  • ***
  • Posts: 2587
  • Country: fr
    • kripton2035 schematics repository
I definitely had stability issues with nodemcu/lua boards.
I reprogrammed them with arduino ide and no more problems...
 

Offline FrankBussTopic starter

  • Supporter
  • ****
  • Posts: 2365
  • Country: de
    • Frank Buss
What kind of stability problems? There are some traps for young players, like the watchdog timer, which can reset the module, if your Lua function runs too long without feeding the dog, or the default 4096 bytes limit for strings, which can be increased at runtime.
So Long, and Thanks for All the Fish
Electronics, hiking, retro-computing, electronic music etc.: https://www.youtube.com/c/FrankBussProgrammer
 

Offline kripton2035

  • Super Contributor
  • ***
  • Posts: 2587
  • Country: fr
    • kripton2035 schematics repository
frequent disconnections, module did not respond after some (varying) time. even if it was continuously sending datas, it stopped.
redid the same program in arduino mode and no problem.
it's not a watchdog problem because it should have restarted to send datas by itself even after a watchdog reset, but it stopped.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf