initial commit
- added readme - added recharge scripts - added dots simple test script - added images, transitions and animations script
This commit is contained in:
commit
666a377e32
7 changed files with 404 additions and 0 deletions
1
.env.example
Normal file
1
.env.example
Normal file
|
@ -0,0 +1 @@
|
|||
DOTS_HOST=fluepdot.local
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.env
|
18
README.md
Normal file
18
README.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Fluepdot Scripts
|
||||
|
||||
Collection of runnable scripts to display stuff on the fluepdot module in the flipdot hackspace
|
||||
|
||||
All scripts should load the hostname(s) for the modules from the `.env` file.
|
||||
|
||||
Requirements for each script may vary but most will use the python-fluepdot library.
|
||||
All requirements should be listed in the main script file.
|
||||
|
||||
|
||||
|
||||
## Notes:
|
||||
|
||||
### Flashing firmware
|
||||
|
||||
```
|
||||
esptool.py -p /dev/ttyUSB0 -b 115200 --before default_reset --after hard_reset --chip esp32 write_flash --flash_mode keep --flash_size detect --flash_freq 40m 0x800 partition_table/partition-table.bin 0xd000 bootloader/bootloader.bin 0x10000 ./flipdot-firmware.bin 0xD000 ota_data_initial.bin
|
||||
```
|
17
dots.py
Normal file
17
dots.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
"""
|
||||
Small sample Script to get size, mode and fonts from the Module and display it's Hostname
|
||||
"""
|
||||
# requires: python-fluepdot, python-dotenv
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from fluepdot import Fluepdot, Mode
|
||||
|
||||
load_dotenv()
|
||||
hostname = os.getenv("DOTS_HOST")
|
||||
|
||||
|
||||
fd = Fluepdot(f"http://{hostname}")
|
||||
#fd.post_text("Current Time:", x=0, y=-1, font="DejaVuSerif16")
|
||||
#fd.clear()
|
||||
print(fd.get_size(), fd.get_mode(), fd.get_fonts())
|
||||
fd.post_text(f"{hostname}", font="DejaVuSans12bw_bwfont")
|
199
images.py
Normal file
199
images.py
Normal file
|
@ -0,0 +1,199 @@
|
|||
"""
|
||||
This file contains animations, stillframes and transitions that can be imported elsewhere.
|
||||
When running this file it will randomly select a transition and a frame to transition to indefinitly.
|
||||
"""
|
||||
# requires: python-fluepdot, python-dotenv
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from src.fluepdot.fluepdot import Fluepdot, Mode
|
||||
from random import choice, sample
|
||||
from itertools import product
|
||||
from time import sleep
|
||||
from typing import List
|
||||
|
||||
DELAY = 5 # hold time for completed animations/frames
|
||||
ANIMATION_DELAY = 0.1 # delay between frames of animations
|
||||
|
||||
flipdot_logo = [
|
||||
" XXXXXX ",
|
||||
" XX X ",
|
||||
" X X ",
|
||||
" X X ",
|
||||
" X XX ",
|
||||
"X X",
|
||||
"X X X X",
|
||||
"X X X X X",
|
||||
"X X XXX X",
|
||||
"X XXX X X X",
|
||||
"X X X X X",
|
||||
" X X XXX X ",
|
||||
" X X ",
|
||||
" X X ",
|
||||
" XX XX ",
|
||||
" XXXXXX ",
|
||||
]
|
||||
|
||||
|
||||
fd1 = Fluepdot(f"http://{ip1}")
|
||||
#fd2 = Fluepdot(f"http://{ip2}", flipped=True)
|
||||
|
||||
def checkerboard(len_x: int, len_y: int, invert: bool = False) -> List[List[bool]]:
|
||||
return [[(j+i+invert)%2==0 for i in range(len_x)] for j in range(len_y)]
|
||||
|
||||
def droplet(fd: Fluepdot):
|
||||
max_x, max_y = fd.get_size()
|
||||
fd.set_mode(Mode.DIFFERENTIAL)
|
||||
frame = [[False for _ in range(max_x)] for _ in range(max_y)]
|
||||
|
||||
cx, cy = max_x//2-.5, max_y//2-.5
|
||||
|
||||
r = 0
|
||||
while r < cx+32:
|
||||
for y in range(max_y):
|
||||
for x in range(int(cx-r-2), int(cx+r+3)):
|
||||
if not (0 <= x < max_x and 0 <= y < max_y):
|
||||
continue
|
||||
frame[y][x] = False
|
||||
dist = ((x-cx)**2+(y-cy)**2)**.5
|
||||
for l in range(5):
|
||||
if r-1-(l**1.4)*4 < dist < r+1-(l**1.4)*4:
|
||||
frame[y][x] = True
|
||||
break
|
||||
r+=2
|
||||
if r > 36:
|
||||
fd_logo_width = len(flipdot_logo[0])
|
||||
s = int(cx+1 - fd_logo_width//2)
|
||||
e = s + fd_logo_width
|
||||
for y in range(max_y):
|
||||
for x in range(s, e):
|
||||
frame[y][x] = flipdot_logo[y][x-s] == "X" or frame[y][x]
|
||||
|
||||
sleep(ANIMATION_DELAY)
|
||||
fd.post_frame(frame)
|
||||
|
||||
def fd_logo(fd: Fluepdot) -> List[List[bool]]:
|
||||
fd.post_text("flipdot e.V.", x=7, y=0, font="fixed_7x14")
|
||||
original_frame = fd.get_frame()
|
||||
frame = []
|
||||
for lrow, frow in zip(flipdot_logo, original_frame):
|
||||
new_row = frow[:-(len(lrow)+2)]+lrow+frow[-2:]
|
||||
frame.append(new_row)
|
||||
return [[c=="X" for c in row] for row in frame]
|
||||
|
||||
def kurhacken(fd: Fluepdot) -> List[List[bool]]:
|
||||
fd.post_text("Kurhecken", x=14, y=-2, font="fixed_10x20")
|
||||
frame = [[c=="X" for c in row] for row in fd.get_frame()]
|
||||
fd.set_mode(Mode.DIFFERENTIAL)
|
||||
for x, y in [(56, 4),(57, 4),(60, 4),(61, 4),(56, 3),(57, 3),(60, 3),(61, 3)]:
|
||||
frame[y][x] = True
|
||||
return frame
|
||||
|
||||
def event37c3(fd: Fluepdot) -> List[List[bool]]:
|
||||
fd.post_text("37c3Unlocked", x=-1, y=-1, font="DejaVuSerif16")
|
||||
frame = [[c=="X" for c in row] for row in fd.get_frame()]
|
||||
return frame
|
||||
|
||||
def event38c3(fd: Fluepdot) -> List[List[bool]]:
|
||||
fd.post_text("38C3 Illegal Instructions", font="DejaVuSerif16")
|
||||
frame = [[c=="X" for c in row] for row in fd.get_frame()]
|
||||
return frame
|
||||
|
||||
def hackumenta(fd: Fluepdot) -> List[List[bool]]:
|
||||
fd.post_text("Hackumenta", x=3, y=-1, font="DejaVuSerif16")
|
||||
frame = [[c=="X" for c in row] for row in fd.get_frame()]
|
||||
return frame
|
||||
|
||||
def wipe_to(fd: Fluepdot, frame: List[List[bool]]):
|
||||
fd.set_mode(Mode.DIFFERENTIAL)
|
||||
current = [[c=="X" for c in row] for row in fd.get_frame()]
|
||||
for i in range(0, len(current[0])):
|
||||
for j in range(len(current)):
|
||||
current[j][i] = frame[j][i]
|
||||
if i+1 < len(current[0]):
|
||||
current[j][i+1] = True
|
||||
sleep(ANIMATION_DELAY)
|
||||
fd.post_frame(current)
|
||||
|
||||
def dither_to(fd: Fluepdot, frame: List[List[bool]]):
|
||||
fd.set_mode(Mode.DIFFERENTIAL)
|
||||
max_x, max_y = fd.get_size()
|
||||
current = [[c=="X" for c in row] for row in fd.get_frame()]
|
||||
coords = [(x,y) for x, y in product(range(max_x), range(max_y)) if current[y][x] != frame[y][x]]
|
||||
while coords:
|
||||
sleep(ANIMATION_DELAY)
|
||||
x, y = choice(coords)
|
||||
coords.remove((x, y))
|
||||
if frame[y][x]:
|
||||
r = fd.set_pixel(x, y)
|
||||
if r.status_code != 200:
|
||||
print(r.text)
|
||||
else:
|
||||
r = fd.unset_pixel(x, y)
|
||||
if r.status_code != 200:
|
||||
print(r.text)
|
||||
|
||||
def push_to(fd: Fluepdot, frame: List[List[bool]]):
|
||||
fd.set_mode(Mode.DIFFERENTIAL)
|
||||
current = [[c=="X" for c in row] for row in fd.get_frame()]
|
||||
while True:
|
||||
sleep(max(ANIMATION_DELAY, 1))
|
||||
for crow, frow in zip(current, frame):
|
||||
for _ in range(3):
|
||||
crow.insert(0, frow.pop(-1))
|
||||
crow.pop(-1)
|
||||
if frow:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
else:
|
||||
fd.post_frame(current)
|
||||
continue
|
||||
break
|
||||
|
||||
def roll_to(fd: Fluepdot, frame: List[List[bool]]):
|
||||
fd.set_mode(Mode.DIFFERENTIAL)
|
||||
current = [[c=="X" for c in row] for row in fd.get_frame()]
|
||||
while frame:
|
||||
sleep(max(ANIMATION_DELAY, 1.5))
|
||||
current.insert(0, frame.pop())
|
||||
current.pop()
|
||||
fd.post_frame(current)
|
||||
|
||||
if __name__ == "__main__":
|
||||
load_dotenv()
|
||||
hostname = os.getenv("DOTS_HOST")
|
||||
fd = Fluepdot(f"http://{hostname}")
|
||||
fd1.clear()
|
||||
animations = [
|
||||
droplet,
|
||||
]
|
||||
transitions = [
|
||||
wipe_to,
|
||||
dither_to,
|
||||
push_to,
|
||||
roll_to,
|
||||
]
|
||||
stillframes = [
|
||||
kurhacken(fd), # Text endpoint
|
||||
checkerboard(*fd.get_size(), invert=False), # Frame endpoint
|
||||
[[True]*115]*16,
|
||||
[[False]*115]*16,
|
||||
#event37c3(fd), # Text endpoint
|
||||
event38c3(fd), # Text endpoint
|
||||
hackumenta(fd),
|
||||
fd_logo(fd), # Text endpoint
|
||||
]
|
||||
#for i, f in enumerate(stillframes):
|
||||
# print(i, len(f), [len(g) for g in f])
|
||||
|
||||
current = stillframes[1]
|
||||
while True:
|
||||
sf = sample(stillframes, k=1)[0]
|
||||
tr = choice(transitions)
|
||||
if sf == current:
|
||||
print("Skipping same frame")
|
||||
continue
|
||||
print(tr.__name__)
|
||||
current = sf
|
||||
tr(fd, sf)
|
||||
sleep(DELAY)
|
87
recharge.py
Normal file
87
recharge.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
"""
|
||||
Script for the flipdot recharge party animation
|
||||
"""
|
||||
# requires: python-fluepdot, python_dotenv
|
||||
import os
|
||||
from time import sleep
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from fluepdot import Fluepdot, Mode
|
||||
|
||||
logo = """
|
||||
|
||||
X
|
||||
XXXXXXXXXXXX XX XXXXXXXXXXX
|
||||
XX XXX XX
|
||||
X XXX X
|
||||
X XXXX X
|
||||
X XXXXXXXX XX
|
||||
X XXXXXXXX XX
|
||||
X XXXX X
|
||||
X XXX X
|
||||
XX XXX XX
|
||||
XXXXXXXXXXX XX XXXXXXXXXXXX
|
||||
X
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# convert text to framebuffer format
|
||||
def textToFramebuffer(text: str, x:int=0, y:int=0, fontname:str="DejaVuSans.ttf", size:int=14) -> str:
|
||||
from PIL import Image, ImageFont, ImageDraw
|
||||
font = ImageFont.truetype(fontname, size)
|
||||
tw, th = font.getsize(text)
|
||||
while th >= 16:
|
||||
print(th)
|
||||
size -= 1
|
||||
font = ImageFont.truetype(fontname, size)
|
||||
tw, th = font.getsize(text)
|
||||
img = Image.new("1", (tw, 16), 0)
|
||||
draw = ImageDraw.Draw(img)
|
||||
draw.text((0, 0), text=text, font=font, fill=1)
|
||||
|
||||
framebuffer: str = ""
|
||||
for y in range(img.height):
|
||||
for x in range(img.width):
|
||||
framebuffer += "X" if img.getpixel((x, y)) == 1 else " "
|
||||
framebuffer += "\n"
|
||||
return framebuffer
|
||||
|
||||
|
||||
def scrl_frm(frame, loop: bool or int = False) -> None:
|
||||
def _extend_frame(line: str) -> str:
|
||||
return line+(" "*loop)+line[0:115]
|
||||
def _pad_frame(line: str) -> str:
|
||||
pad = " "*115
|
||||
return pad+line+pad
|
||||
|
||||
frame = frame.split("\n")
|
||||
length = len(frame[0])+loop if type(loop) == int else len(frame[0])+115
|
||||
|
||||
if type(loop) == int:
|
||||
frame = list(map(_extend_frame, frame))
|
||||
else:
|
||||
frame = list(map(_pad_frame, frame))
|
||||
|
||||
_run_once=True
|
||||
while loop or type(loop)==int or _run_once:
|
||||
_run_once=False
|
||||
for i in range(0, length, 2):
|
||||
print(".\n".join(l[i:i+115] for l in frame))
|
||||
sleep(.1)
|
||||
|
||||
if __name__=="__main__":
|
||||
load_dotenv()
|
||||
hostname = os.getenv("DOTS_HOST")
|
||||
frame = textToFramebuffer("recharge! flipdot e.V.", fontname="DejaVuSans.ttf")
|
||||
fd = Fluepdot(f"http://{hostname}")
|
||||
fd.set_mode(Mode.DIFFERENTIAL)
|
||||
|
||||
logoarr = logo.split("\n")
|
||||
nframe = ""
|
||||
for y in range(len(logoarr)):
|
||||
nframe += logoarr[y] + frame.split("\n")[y]+ "\n"
|
||||
nframe = nframe[:-1]
|
||||
print(nframe)
|
||||
fd.post_scroll_frame_raw(nframe, loop=2)
|
||||
#scrl_frm(nframe, loop=2)
|
81
recharge2.py
Normal file
81
recharge2.py
Normal file
|
@ -0,0 +1,81 @@
|
|||
"""
|
||||
Alternative animation for the flipdot recharge party
|
||||
"""
|
||||
# requires: python-fluepdot, python-dotenv
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from fluepdot import Fluepdot, Mode
|
||||
from time import sleep
|
||||
|
||||
battery = [
|
||||
" XXXXXXXXXXXX XXXXXXXXXXX ",
|
||||
"XX XX ",
|
||||
"X X ",
|
||||
"X X ",
|
||||
"X XX",
|
||||
"X XX",
|
||||
"X X ",
|
||||
"X X ",
|
||||
"XX XX ",
|
||||
" XXXXXXXXXXX XXXXXXXXXXXX "]
|
||||
|
||||
lightning=[" X ",
|
||||
" XX ",
|
||||
" XXX ",
|
||||
" XXX ",
|
||||
" XXXX ",
|
||||
"XXXXXXXX "]
|
||||
|
||||
def get_lightning(level: int) -> str:
|
||||
if level < 0:
|
||||
level = 0
|
||||
elif level > 12:
|
||||
level = 12
|
||||
|
||||
rtn = "\n".rjust(19)+"\n".rjust(19)
|
||||
|
||||
for i in range(0, level):
|
||||
line = lightning[i] if i < len(lightning) else lightning[len(lightning)-i-1][::-1]
|
||||
rtn = line.rjust(19)+"\n"+rtn
|
||||
while rtn.count("\n") < 16:
|
||||
rtn="\n".rjust(19)+rtn
|
||||
return rtn
|
||||
|
||||
def get_battery(level: int) -> str:
|
||||
lightning = get_lightning(level).split("\n")[:-1]
|
||||
|
||||
rtn = ""
|
||||
for ln in range(0, len(lightning)):
|
||||
if ln < 3:
|
||||
rtn+=lightning[ln].ljust(30)
|
||||
elif ln > 2+len(battery):
|
||||
rtn+=lightning[ln].ljust(30)
|
||||
else:
|
||||
for cind in range(0, len(battery[0])):
|
||||
try:
|
||||
rtn += "X" if battery[ln-3][cind] == "X" or lightning[ln][cind] == "X" else " "
|
||||
except IndexError:
|
||||
rtn += " "
|
||||
rtn += "\n"
|
||||
|
||||
return rtn
|
||||
|
||||
if __name__=="__main__":
|
||||
load_dotenv()
|
||||
hostname = os.getenv("DOTS_HOST")
|
||||
fd = Fluepdot(f"http://{hostname}")
|
||||
fd.set_mode(Mode.DIFFERENTIAL)
|
||||
l, h = fd.get_size()
|
||||
while True:
|
||||
for i in range(0, 12):
|
||||
bat = get_battery(i).split("\n")[:-1]
|
||||
bat = list(map(lambda x: x.rjust(l), bat))
|
||||
bat = "\n".join(bat)+"\n"
|
||||
fd.post_frame_raw(frame=bat)
|
||||
sleep(.5)
|
||||
for i in range(12, 0, -1):
|
||||
bat = get_battery(i).split("\n")[:-1]
|
||||
bat = list(map(lambda x: x.rjust(l), bat))
|
||||
bat = "\n".join(bat)+"\n"
|
||||
fd.post_frame_raw(frame=bat)
|
||||
sleep(.5)
|
Loading…
Add table
Add a link
Reference in a new issue