Last active
August 16, 2020 10:48
-
-
Save meisterluk/5010dc2f419d9b1d199146d0eadd3f53 to your computer and use it in GitHub Desktop.
Controlling the Raspberry Pi 3 Model B Sense HAT 8×8 LED matrix from Lua 5.3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local M = {_TYPE='module', _NAME='sensehat_pixels', _VERSION='0.0.1'} | |
-- Sleep this thread | |
function sleep(n) | |
io.popen("sleep " .. tostring(n)):read() | |
end | |
-- Split a string into lines | |
function lines(str) | |
local t = {} | |
local function helper(line) | |
table.insert(t, line) | |
return "" | |
end | |
helper(str:gsub("(.-)\r?\n", helper)) | |
return t | |
end | |
-- Does file $path exist? | |
function file_exists(path) | |
local fd = io.open(path, "r") | |
if fd ~= nil then | |
io.close(fd) | |
return true | |
else | |
return false | |
end | |
end | |
-- List files and folders in $path | |
function list_directory(path) | |
return io.popen([[ls -1 ]] .. path):lines() | |
end | |
-- Find sense hat device | |
function sense_hat_device_path() | |
local target_name = "RPi-Sense FB" | |
for node in list_directory("/sys/class/graphics") do | |
if string.sub(node, 1, 2) == "fb" then | |
local name_path = "/sys/class/graphics/" .. node .. "/name" | |
if file_exists(name_path) then | |
local fd = io.open(name_path, "r") | |
local content = fd:read() | |
fd:close() | |
if content == target_name then | |
return "/dev/" .. node | |
end | |
end | |
end | |
end | |
end | |
-- Represent a given color as RGB565 byte string | |
function color_to_bin(rgb) | |
if rgb:len() ~= 6 then | |
error("color " .. rgb .. " is not a 6-digit hex color") | |
end | |
local r = tonumber(rgb:sub(1, 2), 16) | |
local g = tonumber(rgb:sub(3, 4), 16) | |
local b = tonumber(rgb:sub(5, 6), 16) | |
r = (r >> 3) & 0x1F | |
g = (g >> 2) & 0x3F | |
b = (b >> 3) & 0x1F | |
local comb = (r << 11) + (g << 5) + b | |
return string.pack("H", comb) | |
end | |
-- Given a string with 8 lines with length 8 each, | |
-- each byte represents a color. The byte to color | |
-- map is given by the second argument. | |
function pixel_from_picture(pic, map) | |
local data = {} | |
local lineno = 1 | |
for _, line in pairs(lines(pic)) do | |
if line:len() == 0 then | |
goto next | |
end | |
if line:len() ~= 8 then | |
error("line must have length 8") | |
end | |
for i = 1, #line do | |
local total_index = (lineno - 1) * 8 + i | |
if map[line:sub(i, i)] == nil then | |
error("character '" .. line:sub(i, i) .. "' could not be mapped to color") | |
end | |
data[total_index] = map[line:sub(i, i)] | |
end | |
lineno = lineno + 1 | |
::next:: | |
end | |
return data | |
end | |
function default_colors() | |
local color_table = { | |
["white"] = "FFFFFF", ["silver"] = "C0C0C0", ["gray"] = "808080", | |
["black"] = "000000", ["red"] = "FF0000", ["maroon"] = "800000", | |
["yellow"] = "FFFF00", ["olive"] = "808000", ["lime"] = "00FF00", | |
["green"] = "008000", ["aqua"] = "00FFFF", ["teal"] = "008080", | |
["blue"] = "0000FF", ["navy"] = "000080", ["fuchsia"] = "FF00FF", | |
["purple"] = "800080" | |
} | |
color_table[" "] = color_table["black"] | |
color_table["X"] = color_table["teal"] | |
color_table["0"] = color_table["white"] | |
color_table["1"] = color_table["blue"] | |
return color_table | |
end | |
function letter_pics() | |
return { | |
["E"] = [[ | |
000000 | |
000000 | |
00 | |
000000 | |
000000 | |
00 | |
000000 | |
000000 | |
]], | |
["H"] = [[ | |
00 00 | |
00 00 | |
00 00 | |
000000 | |
000000 | |
00 00 | |
00 00 | |
00 00 | |
]], | |
["I"] = [[ | |
00 | |
00 | |
00 | |
00 | |
00 | |
00 | |
00 | |
00 | |
]], | |
["L"] = [[ | |
00 | |
00 | |
00 | |
00 | |
00 | |
00 | |
000000 | |
000000 | |
]], | |
["O"] = [[ | |
0000 | |
000000 | |
00 00 | |
00 00 | |
00 00 | |
00 00 | |
000000 | |
0000 | |
]], | |
} | |
end | |
-- Given a map with indices 1..64, each index | |
-- is mapped to a color to be set on raspberry | |
-- pi's sense hat | |
function set_pixels(pixels) | |
local fd = io.open(sense_hat_device_path(), "w") | |
for idx, color in pairs(pixels) do | |
fd:seek("set", 2 * (idx - 1)) | |
fd:write(color_to_bin(color)) | |
end | |
end | |
-- clear pixels on the RPi Sense hat | |
function clear_pixels() | |
local color_table = {} | |
for i = 1, 64 do | |
color_table[i] = "000000" | |
end | |
set_pixels(color_table) | |
end | |
local text = "HELLO" | |
local pics = letter_pics() | |
clear_pixels() | |
for i = 1, #text do | |
sleep(2) | |
print(text:sub(i, i)) | |
local c = pixel_from_picture(pics[text:sub(i, i)], default_colors()) | |
set_pixels(c) | |
end | |
sleep(2) | |
--clear_pixels() | |
colortable = pixel_from_picture([[ | |
00 00 | |
00 00 | |
00 00 | |
00 00 | |
0000 | |
]], default_colors()) | |
set_pixels(colortable) | |
clear_pixels() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment