Lua interpreter example?

Status
Not open for further replies.

anthonysavatar

Active member
I'd like to be able to run some scripts from an SD card to control application behavior. Has anyone tried this using Lua on the Teensy 3.6?

I'm aware of eLua but don't want to run the whole Teensy in Lua, just application specific code. To be more specific I'd like to write little games or apps in Lua to control a display connected to the Teensy. I'd also consider using Forth but figured Lua would be more accessible for others.
 
I am also interested in extending teensy sketches with lua.

I have tried creating Arduino libs out of lua 5.3.4 and 5.3.2 but keep running into compile errors on some newer C++ libraries and types (signals and locale, for example). I will try again later with an older version of lua.
 
Thanks for this. I had not heard of eLua. I took a look and started trying to compile it and made some progress. I will have to get back to this later.
 
I'm interested in what you discover :) Particularly the size it adds to your program, it's memory usage and performance. i.e. everything :) Is it viable for extending teensy sketches?
 
I was able to build and execute lua 5.3.4. I haven't figured out how to incorporate lua 'files' into my project yet though, so I am just executing strings.

Once I figure out how to add files to my project I will clean it up and do some more investigation. Unfortunately I am away and will be back next week.
 
For anyone interested in this I've got an example of running Lua 5.1.5 on a Teensy 3.6 here: https://github.com/AnthonyDiGirolamo/lua-teensyduino

It contains a simple repl and examples of how to run lua code from strings or the sdcard (using SdFat).

An example of making your own c++ functions available to Lua can be seen in the sdwrapper files.

It uses Lua 5.1.5 with the following patches:
Note: this isn't a port of eLua or nodemcu. It's just an example of how to add Lua scripting to your own Teensy 3.6 project.

Please let me know if you have any feedback or make something with it!
 
For loading lua scripts, you'll probably want to use a bundler script like this one, then turn the result into a char array in a header file that you'll #include in your C source.

That bundler is a little crude. You'd ideally want something like Browserify in the JS world, but that entails properly parsing the source files to look for require calls, which was overkill for my purposes.

Note that the bundler here relies on require to avoid polluting the global namespace, because lulpeg is a library which should be encapsulated. For your own scripts, you could rely on globals to share values between files, and wrap each file in a do ... end block to keep the locals scoped per file. Just make sure the files are bundled in the right order according to their dependencies.
 
Last edited:
Anthony, that's excellent work! I'm currently trying to repackage your lua-teensyduino into an Arduino library that can be added to any project that needs a Lua interpreter.

I've found that if I factor out the SD portions, it's quite portable (basically.working on Teensy 3.2, 3.6 (and I assume 3.5, though I don't have one of those to test), as well as Adafruit itsybitsy M4 and Trinket M0. I've also tried to get it to work on ESP32, but it seems to be crashing on boot, that will take a little effort.

Work in progress here:

https://github.com/blackketter/LuaArduino

I'm hoping to re-integrate the SD support to make it optional, as well as add some functions to make it easier to hook C functions to be called from Lua.

Feedback most welcome!
 
Thanks for putting that together! I'm happy to help get it working on other platforms. Making it easy for users to load Lua files from other sources besides strings will be good. Like external flash or SD. I'll follow up in your repo.
 
Lua_on_Teensy.jpg

Got it finally. :cool:

Lua 5.3.5 on Teensy 4.0. Hacked under VisualTeensy V0.9.7.1, GNU ARM Embedded 8 2019-q3-update

Still more work to do, to have this really dressed up, but for now:

Enjoy!
 
Last edited:
Teensy 4.0, Lua, ChibiOS -- Full Build/Development Environment for Visual Studio Code

Updated to include ChibiOS with the SPI library integrated into the Teensy 4 core.

This link includes the entire build environment minus Visual Studio Code.

I like simple and straightforward--no crazy PlatformIO stuff, no special builders, no extra baggage, just a Makefile, a few directories and you're off to the races. Hopefully this will ease the pain and agony I went through trying to make the Teensy 4.0 shine. My only real gripe so far is why on earth was the built-in LED placed on the SPI SCLK pin?


Cheers!
 
Last edited:
Smoke'n...

Updated again. Now includes the SCE screen editor, ChibiOS and Lua 5.3.5 interpreter.

This link is still good.

A lot of hours poured into getting things this far. Hopefully someone can take it and run with it.

So far things appear stable and quite functional. Been learning Lua hands-on as a controller scripting language.

Sure fun to see a plan come together. :)


Cheers!
 
Sounds great, @Dog-One! Can you give a little background on what changes you had to make to get it to build? I'd like to get it working under Arduino and PlatformIO.
 
Lua 5.3.5 Mods

Sounds great, @Dog-One! Can you give a little background on what changes you had to make to get it to build? I'd like to get it working under Arduino and PlatformIO.

Let me see here...

Teensy-CNSL.jpg

There are two ways to go about driving the interpreter: The first method is to use the Lua "main" function (rename it of course), send it "-E" "-i" and let it run the doREPL loop. If you go this route, you'll probably want to stub out usb_serial_gets and a usb_serial_puts functions, then remap:
Code:
// int usb_serial_puts(const char *str);
// char *usb_serial_gets(char *str, int n);

// Override this to redirect interactive session to hardware I/O
#if 0
#define lua_readline(L,b,p) \
        ((void)L, fputs(p, stdout), fflush(stdout),  /* show prompt */ \
        fgets(b, LUA_MAXINPUT, stdin) != NULL)  /* get line */
#endif

extern int usb_serial_puts(const char *);
extern char *usb_serial_gets(char *, int);

#define lua_readline(L,b,p) \
        ((void)L, usb_serial_puts(p), usb_serial_gets(b, LUA_MAXINPUT) != NULL)
Or...
My second approach was to just use the Lua dostring() and stub out:
Code:
  // Setup stdout console primitive (enable direct, i.e. printf, etc)
  // Code added to send a carriage return following any newline found
  // in buffer--needed for raw-mode I/O.
  int _write(int file, char *ptr, int len) {
    (void) file;
    const char nl = '\r';
    int idx = 0;
    while (idx < len) {
      usb_serial_write(&(ptr[idx]), 1);
      if (ptr[idx] == '\n') {
        usb_serial_write(&nl, 1);
      }
      idx++;
    }
    usb_serial_flush_output();
    return len;
  }
Then you can build your own entry point like:
Code:
// Lua entry point to run ramfile script
int lua_main(void) {
  int idx;
  lua_State *L = luaL_newstate();  // create state

  // Register custom C functions here
  lua_register(L, "pinset", pinset);  // ...
  lua_register(L, "setRTC", setRTC);
  lua_register(L, "wait", wait);
  lua_register(L, "sendSPI", sendSPI);

  lua_pushboolean(L, 1);
  lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
  luaL_openlibs(L);  /* open standard libraries */
  print_version();
  dostring (L, ramfile, "ramfile.lua");
  lua_close(L);
  return EXIT_SUCCESS;

To get everything to build, the only major changes I made were to the functions in the oslib; everything else I left active:
Code:
static const luaL_Reg syslib[] = {
  {"clock",     os_clock},
  {"date",      os_date},
  {"difftime",  os_difftime},
//  {"execute",   os_execute},
  {"exit",      os_exit},
//  {"getenv",    os_getenv},
//  {"remove",    os_remove},
//  {"rename",    os_rename},
  {"setlocale", os_setlocale},
  {"time",      os_time},
  {"tmpname",   os_tmpname},
  {NULL, NULL}
};

Still toying with the os_exit() as this basically hard crashes the controller. I left it in as sort of an emergency stop button if I need it.

Keep in mind Lau still thinks it has a full-blown OS living underneath it, so some functions are going to fail--I have no file system for example.

There are some #define directives for LINUX, POSIX, etc. I didn't use any of those and just let the preprocessor pick its way through the header files. Best I can tell the defaults are fairly sound for bare-metal hardware. I did declare #define LUA_32BITS, but could probably work around that if I really needed to. For what I'm doing, 32-bits is plenty accurate for integers and floats. I also removed the luac compiler generation as this really isn't needed if you want a true run-time interpreter.

For those not really in-the-know, Lua uses the stack for everything and it processes scripts as chunks. This later attribute is why I moved away from a fully interactive interpreter. In interactive mode, each line is a chunk instead of the whole group of lines. Think of chunks as fully independent constructs of processing--you break them apart and the pieces become independent and no longer work together as a whole.

What I've done so far is quite usable on the Teensy 4.0, but it could be better. For example, I'm really not using ChibiOS to it's full advantage--Lua coroutines don't do SMP (by design of course, but this could be implemented in a future version) and I could never get the ChibiOS heap management to work at all, so I disabled it and used the compiler malloc, realloc and free functions instead. My implementation of a ramfile is kind of lame, but it works. I'd rather have a file system that can lay on top of RAM and flash and be able to put scripts in either place on the fly. Not sure how much code would be necessary to make this all work. I'll keep investigating and see what I can come up with.

Overall, I spent a lot more time getting SCE ported to this platform than I did Lua. SCE is written in C but it's all in Spanish and I google translated tons of comments to get to a point where I understood how it worked. I then stripped everything out of it not needed to work with PuTTY. Before embarking on this task, I did look at nano and other screen editors first. They all seem tightly hooked into an OS and were still bloated beyond belief. SCE works nicely now. It gives you a very functional screen editor that you can plop in a Lua script, ESC, ESC, to save and run. And with this editor, now you have a means to work with an entire Lua chunk which turns out to be a big plus (bigger than I thought at first).

The build environment I took from VisualTeensy which generates some json files used by Visual Studio Code and a Makefile. Once I had that, I moved things around more to my liking, replaced the GNU ARM compiler with the latest version and broke all dependencies from the Arduino IDE and Teensyduino. My template no longer needs VisualTeensy either. You're all free to beat me up for this, but I wanted something self-contained focused on Teensy 4.0 development. Five years from now, I can unzip this file and recompile exactly what I have today and it will still work. This is a lesson I had to learn the hard way before and won't make the same mistake again. Visual Studio Code is about the only real dependency I'm locked into and I'm pretty sure if I had to move to emacs on a Linux machine I could make things work again in a few days. Basically I have an environment where I feel comfortable buying a couple dozen Teensy 4.0s and know I can build and load code to them without any outside forces derailing my attempt.

About using Lua as a language to drive a micro-controller...

I'm feeling pretty good about my decision. I see a lot of Python out there, some BASIC, various precompiled shells and scripts, but I like this Lua stuff. It's new to me, so maybe when the new-car smell fades I won't feel the same, but for now, I think it will work great and do what I want, they way I want. For those that haven't dipped their toe in the Brazilian moonlight, you may want to try it. And when you tie into a micro-controller such as the Teensy 4.0, it might just occur to you (as it did me), this is powerful stuff. I'm certain I could whip out a script to control a HVAC system with no trouble at all. And I could do it now without anything but a serial connection. It's not everyday you run into a situation where less really is more.

I have to say a word about the Teensy 4.0...

Teensy4.0_is_Alive.jpg

The folks at PJRC really out-did themselves with this one. I have used Cypress PSoC devices for years due to their ability to program logic into the hardware. Then just recently, Cypress went off in a different direction with their PSoC 6 devices and IoT. They pretty much gave up on the integrated progammable logic and started focusing on the power of the ARM Cortex processor. All well in good for some, not so much for others, me being one of the others. Okay fine Cypress, you want to change gears and leave your customers flapping in the breeze, do it. So I went on the hunt. I tried the LinkIt device, the NanoPi and a couple of other ARM based devices. Nothing really fit what I was looking for. Then one bright day I went browsing the SparkFun website and came across the Teensy line of micro-controllers. Looky what I found, this shiney new Teensy 4.0. It's got RAM, Flash, speed, it's definitely Teensy and relatively inexpensive too. I think we have only scratched the surface of what this device has to offer. Maybe it's just me, but trying to shoehorn this device into the Arduino box is... Well... One way to do it. Could there be another...?
 
Last edited:
Status
Not open for further replies.
Back
Top