I was struggling to find a simple Lua graphic library. Love2D appears to be well regarded, but I wanted to find something that I could get up and running fast.
An old 1980’s graphic technology called curses has been available for years in most languages and I was familiar with it from C and Python.
In this blog I wanted to shared an example of using the Lua curses library to read and write Raspberry Pi general purpose I/O (GPIO).
To install Lua on a Raspberry Pi:
sudo apt-get update sudo apt-get install lua5.1 sudo apt-get install liblua5.1-0-dev -- development files, need by LuaRocks sudo apt-get install lua-socket sudo apt-get install luarocks -- package manager for Lua modules sudo luarocks install luasocket
Lua has a package manager called luarocks, (this is similar to pip on Python), where you can install custom libraries or packages on the Pi.
There are a number of choices on how Lua can access Pi GPIO pin. I found that the lua-periphery library to be a reliable option. The Lua version of curses is not 100% compatible to the C version but it’s close.
To install these libraries enter:
sudo luarocks install lua-periphery sudo luarocks install curses
Raspberry Pi Hardware
I used a Pimoroni Explorer Hat because it has some built in colored LEDs, but you could easily use some LEDs and resistors and wire your own equivalent setup.
For some details on how to use the Lua Raspberry Pi GPIO library see: https://funprojects.blog/2019/04/20/lua-and-raspberry-pi/
The Lua Curses App
My goal was to create a simple GUI with a title and a footer with the key commands, then show the values on the screen.
To use colored text there are a few steps that are required:
- enable color (curses.start_color())
- define some color pairs (curses.init_pair)
- create an attribute variable that is defined by a color pair(a_red = curses.color_pair(4))
Then use the attribute “ON” function to set the color (stdscr:attron(a_red)).
The mvaddstr function is used to write text to position on the screen object. (stdscr:mvaddstr(2, 5,”SET RASPBERRY PI LEDS” )).
Below is my code to setup 4 LED outputs, and use the keys 1,2,3 and 4 to write to these outputs. The “q” key is used to exit the code.
-- A Lua curses example with some Raspberry Pi Data -- Define Rasp Pi variables local GPIO = require('periphery').GPIO local gpio_in = GPIO(10, "in") local led1 = GPIO(4,"out") local led2 = GPIO(17,"out") local led3 = GPIO(27,"out") local led4 = GPIO(5,"out") led1:write(1) led2:write(1) led3:write(1) led4:write(1) -- Define curses local curses = require 'curses' curses.initscr() curses.echo(false) -- not noecho ! local stdscr = curses.stdscr() -- the screen object -- setup color pairs and attribute variables curses.start_color() curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLACK) curses.init_pair(3, curses.COLOR_BLUE, curses.COLOR_BLACK) curses.init_pair(4, curses.COLOR_YELLOW, curses.COLOR_BLACK) curses.init_pair(5, curses.COLOR_RED, curses.COLOR_BLACK) curses.init_pair(6, curses.COLOR_GREEN, curses.COLOR_BLACK) a_rw = curses.color_pair(1) a_white = curses.color_pair(2) a_blue = curses.color_pair(3) a_yellow = curses.color_pair(4) a_red = curses.color_pair(5) a_green = curses.color_pair(6) stdscr:clear() -- Create a background ncols = curses.cols() nrows = curses.lines() -- Create a top and bottom color strip stdscr:attron(a_rw) -- set the fore/background colors for i=0, (ncols - 1), 1 do -- write a top and bottom strip stdscr:mvaddstr(0,i, " ") stdscr:mvaddstr(nrows -1,i, " ") end stdscr:mvaddstr(0,0, " Curses Lua Dynamic Text Example") stdscr:mvaddstr((nrows -1), 0, " Key Commands: q - to quit, 1,2,3,4 - to toggle LED") -- Add the main screen static text stdscr:attron(a_white) -- set the fore/background colors stdscr:mvaddstr(2, 5,"SET RASPBERRY PI LEDS" ) for i=1,4,1 do stdscr:mvaddstr(4+ i, 5, "LED " .. tostring(i) .. " : " ) end stdscr:refresh() local c = stdscr:getch () while c ~= 113 do -- 113 = q ,quit if c == 49 then led1:write(not led1:read()) end if c == 50 then led2:write(not led2:read()) end if c == 51 then led3:write(not led3:read()) end if c == 52 then led4:write(not led4:read()) end -- show the inputs stdscr:attron(a_blue) stdscr:mvaddstr(5, 15, tostring(led1:read() ) .. " " ) stdscr:attron(a_yellow) stdscr:mvaddstr(6, 15, tostring(led2:read() ) .. " " ) stdscr:attron(a_red) stdscr:mvaddstr(7, 15, tostring(led3:read() ) .. " " ) stdscr:attron(a_green) stdscr:mvaddstr(8, 15, tostring(led4:read() ) .. " " ) c = stdscr:getch () end curses.endwin()
Some Final Comments
Unfortunately I found the Lua curses documentation to be quite weak and there were very few examples.
My only major stumbling block was to find a stdscr.nodelay() function that allows the code to continue without waiting for a key stroke. This feature exists in the Python and C libraries.