I am currently in the process of building my own house (see my blog on ebuild.co.uk) and this is taking up most of my free time, but I am looking at ESP8266-based solutions for Home Automation, and I decided to go the Lua route. Hence it was time to get to grips with yet another language, the NodeMCU eLua variant, and because of my previous interests, I've also been taking a deep-dive into the NodeMCU internals. Overall, I am extremely impressed with eLua, the ESP 8266 and how the NodeMCU people have integrated these into an excellent offering.
The Lua runtime system (RTS) is in many ways similar to that of PHP, but its also a lot leaner and faster, and the eLua variant seems to be extremely well tuned to this type of embedded application. Nonetheless, application developers do have to work within the system constraints and in particular:
- The available application RAM is just under 22K on the NodeMCU 0.9.5 build
- The file system space is under 60K on the 512Kb Flash variants like the ESP-01
An example of the sort of thing that I am talking about is that developers should have some understanding of the following:
- Lua uses a Mark and Sweep Garbage Collector which is extremely well suited to this small-memory type of RTS, and that fully dereferencing any object or string within the application will make it memory available after the next GC sweep
- Lua is an incremental compile system (like PHP), but unlike PHP the opcode arrays are a lot more compact (a typical line of Lua will generate maybe 3 or so 4-btye instructions. Once compiled these are stored in RAM, but unlike PHP the opcode arrays are just another object as far as GC is concerned, so the memory can be reclaimed for other use if properly dereferenced.
- The Lua RTS treats compiled and source files as largely interchangeable. The only difference is that looks for a standard <ESC>Lua header as the first 4 bytes and if found it uses the (very fast) oparray loader to load code from Flash instead of invoking the compiler. So the ".lua" or ".lc" extension is just user-sugar as this snippet shows, you can even have a compiled init.luaCode: Select all
file.open("test.lua","w")
file.writeline([[collectgarbage();print("Compile:",node.heap()); file.remove("init.lua")]])
file.close()
node.compile("test.lua")
file.remove("init.lua"); file.rename("test.lc","init.lua")
node.restart() - Loading compiled functions from Flash is pretty fast (~1mSec). ( Those who know C have a look at app/lua/lundump.c which does this.) So you can do tricks such as using a setmetatable to declare on __index autoloader, and this gives you the ability to pretty transparently load methods in from Flash (and GC them when done) with minimal performance impact. This enables you to fit pretty big applications into the 21Kb RAM limit. (I am developing a little framework based on this but this is the subject of a separate topic.