Lua on Embedded Systems
Quoting the creators: "Lua is a powerful, fast, lightweight, embeddable scripting language." Lightweight means, that it takes about 150-250 kBytes on a PC. This is still a challenge for small embedded systems running Nut/OS, specifically for the 8-bit AVR targets like Ethernut 1 and 2.
In a first attempt, Laszlo Parrag succeeded in 2008 with implementing a subset, that runs small Lua scripts on an ARM7 based board. The source code of a Lua script had been stored in a local character array of a sample Nut/OS application. After adding a few minor modifications, the Lua libraries had been added to the official Nut/OS distribution, enabled for ARM CPUs only. Not much happened to it since June 2009, when Nut/OS 4.9.2 beta became available. This version is able to run a stand-alone Lua interpreter on most target systems, including 8-bit AVRs. However, due to memory limitations, not all targets are able to offer the full set of Lua standard libraries. The developer can use the Nut/OS Configurator to enable or disable specific parts.
Visit www.lua.org to find out more about Lua.
The Nut/OS Configurator had been enhanced with several items to adapt Lua to the specific platform and the needs of the application. Btw. actually the Configurator itself is driven by Lua scripts, and only these scripts had been modified to add these new functions. But that's a different story.
The following screenshot shows the initial configuration. All Lua default libraries are disabled, parser and dump are not excluded. With these settings Nut/OS can run basic Lua source code.
Disabling the libraries doesn't mean, that these libraries are not available. The Configurator will still build them, but they will not be loaded automatically by calling luaL_openlibs. Instead, the Nut/OS application can load individual libraries by calling the related initialization function like luaopen_io or luaopen_string.
Enabling Floating Point Support
In general Lua numbers are floating point values by default. This requires some more memory space and, more important, significantly slows down execution on embedded systems without floating point hardware. Therefore, number are integer by default in Lua for Nut/OS.
You probably noticed that the item Floating Point Numbers is greyed out, which means that you can't enable it without providing a specific requirement. In this case you must enable Nut/OS floating point support for file streams first. This will allow Lua to read and write floats.
Excluding Parser and Dump
The parser allows to directly execute Lua source code. Well, not really directly. In fact the parser translates the source code to Lua binary code first. This step could be done on a desktop computer and only the binaries may be uploaded to the target. As you can imagine, this can save a lot of RAM.
The Lua dump allows to display Lua binary code and is typically not required by the final firmware. Excluding it saves a few kilobytes.
Unlike standard libraries, Lua parser and Lua dump will be completely removed, if excluded. There is no way to load them by function calls in the application during runtime.
The highlighted configuration item in the screenshot above doesn't represent a library. Instead it is an option of the I/O library and will be removed from it unless enabled. More on this later.
A standalone interpreter had been created as a Nut/OS sample application to demonstrate the capabilities. It will read Lua source code and print out results on the RS-232 port, which is available on most embedded boards.
The interpreter uses the new EdLine module of the Nut/OS Gorp library. It is not yet documented. For now, have a look to the source code.
There is no socket library available yet. Instead, the standard I/O library had been extended by two additional functions. They allow to write basic TCP servers and clients.
The Lua code for a client connecting port 80 at IP address 192.0.0.2 may look like this:
c = io.connect("192.0.0.2:80", "r+b") c:write("Hello!\r\n") c:flush() r = c:read() c:close()
The related TCP server may look like this:
c = io.accept("80", "r+b") r = c:read() c:write("Hello to you too!\r\n") c:flush() c:close()