Skip to content

Instantly share code, notes, and snippets.

@meisterluk
Last active January 3, 2021 01:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save meisterluk/d6945cb013091053b714d4b7b622401f to your computer and use it in GitHub Desktop.
Save meisterluk/d6945cb013091053b714d4b7b622401f to your computer and use it in GitHub Desktop.
Creating a firework with the Raspberry Pi 3 Model B Sense HAT 8×8 LED matrix and Lua 5.3
-- load pixels.lua (attached as part of this gist)
package.path = package.path .. ';?.lua'
local hat = require('rpi_sensehat_pixels')
-- a generic color table
local colortable = {
[0] = '0000FF',
[1] = '00FF00',
[2] = 'FF0000',
[3] = '1354AA',
[4] = 'AAAAAA',
[5] = 'FFFF00',
[6] = '00FFFF',
[7] = 'FFCC33',
}
-- set pixel at led_id to color
local set_pixel = function (led_id, color)
local fd = io.open(hat.sense_hat_device_path(), "w")
fd:seek("set", 2 * (led_id - 1))
fd:write(hat.color_to_bin(color))
fd:close()
end
-- Convert a (x, y) point on this 8×8 LED matrix
-- into a LED id. The x=0 y=0 is at the bottom left
-- and x=1 y=0 is one to the right.
local xy_to_id = function (x, y)
return 57 + x - 8 * y
end
-- is the given point (x, y) on the circle with
-- center (cx, cy) and radius r?
local point_is_on_circle = function (cx, cy, r, x, y)
local in_big_circle = (x - cx) * (x - cx) + (y - cy) * (y - cy) - r * r <= 1
local in_small_circle = (x - cx) * (x - cx) + (y - cy) * (y - cy) - (r-1) * (r-1) <= 1
return in_big_circle and not in_small_circle
end
-- draw the explosion circle of a firework
-- with color and center (cx, cy) at time t ∈ {0, 1, …, 4}
local draw_explosion = function (color, cx, cy, t)
if t == 0 then
set_pixel(xy_to_id(cx, cy), color)
end
if t > 0 and t <= 4 then
for x = 0, 7 do
for y = 0, 7 do
if point_is_on_circle(cx, cy, t, x, y) then
set_pixel(xy_to_id(x, y), color)
end
end
end
end
end
-- draw a firework at some random pixel of Sense HAT
-- (actually, the rocket rises at least 2 pixels,
-- so it is not arbitrary)
local draw_firework = function ()
hat.clear_pixels()
local cx = math.random(8) - 1
local cy = math.random(6) + 1
local color = colortable[math.random(8) - 1]
-- draw the rising rocket
for t = 0, (cy - 1) do
set_pixel(xy_to_id(cx, t), color)
hat.sleep(0.1)
hat.clear_pixels()
end
-- draw the explosion circle
for t = 0, 4 do
draw_explosion(color, cx, cy, t)
hat.clear_pixels()
end
end
for e = 0, 14 do
draw_firework()
hat.sleep(0.6)
end
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
return {
sleep = sleep,
file_exists = file_exists,
list_directory = list_directory,
sense_hat_device_path = sense_hat_device_path,
color_to_bin = color_to_bin,
pixel_from_picture = pixel_from_picture,
default_colors = default_colors,
letter_pics = letter_pics,
set_pixels = set_pixels,
clear_pixels = clear_pixels
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment