|
@@ -0,0 +1,337 @@ |
|
|
|
|
|
''' |
|
|
|
|
|
Live typing demo for Maker Faire Berlin 2018 |
|
|
|
|
|
|
|
|
|
|
|
This class enables visitors to type on a USB-Keyboard connected to the |
|
|
|
|
|
control laptop and have the typed text appear on the 30x30 |
|
|
|
|
|
WS2812-LED-Display immediately (5 letters per line with three lines). |
|
|
|
|
|
|
|
|
|
|
|
This demo was programmed during the exhibition. |
|
|
|
|
|
|
|
|
|
|
|
By using pygame, it also provides a live view of the current display |
|
|
|
|
|
for the operator - after all, we had a lot of kids on the faire and |
|
|
|
|
|
needed to make sure they didn't type anything 'spicy' ;-) |
|
|
|
|
|
|
|
|
|
|
|
Also, it was kinda cool to be able to see what's being displayed right |
|
|
|
|
|
now as opposed to having to walk around to see the front of the |
|
|
|
|
|
display. It allowed for some 'fake artificial intelligence' chatting |
|
|
|
|
|
with visitors ;) |
|
|
|
|
|
|
|
|
|
|
|
@author Thomas Hoffmann |
|
|
|
|
|
@date 27-05-2018 |
|
|
|
|
|
|
|
|
|
|
|
''' |
|
|
|
|
|
|
|
|
|
|
|
import pygame, time |
|
|
|
|
|
import pygame.display |
|
|
|
|
|
from random import randint |
|
|
|
|
|
|
|
|
|
|
|
from neopixel import * |
|
|
|
|
|
from PIL import ImageDraw, ImageFont, Image |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### set up the LED strip |
|
|
|
|
|
LED_COUNT = 900 # Number of LED pixels. |
|
|
|
|
|
LED_PIN = 21 # GPIO pin connected to the pixels (18 uses PWM!). |
|
|
|
|
|
LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) |
|
|
|
|
|
LED_DMA = 10 # DMA channel to use for generating signal (try 10) |
|
|
|
|
|
LED_BRIGHTNESS = 255 # Set to 0 for darkest and 255 for brightest |
|
|
|
|
|
LED_INVERT = False # True to invert the signal (when using NPN transi |
|
|
|
|
|
LED_CHANNEL = 0 # set to '1' for GPIOs 13, 19, 41, 45 or 53 |
|
|
|
|
|
LED_STRIP = ws.WS2811_STRIP_GRB |
|
|
|
|
|
|
|
|
|
|
|
strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_STRIP) |
|
|
|
|
|
strip.begin() |
|
|
|
|
|
|
|
|
|
|
|
print ('Press Ctrl-C to quit.') |
|
|
|
|
|
|
|
|
|
|
|
#### set up pygame |
|
|
|
|
|
pygame.init() |
|
|
|
|
|
pygame.display.init() |
|
|
|
|
|
screen = pygame.display.set_mode((50,50)) # create a 50x50 px window |
|
|
|
|
|
|
|
|
|
|
|
## list of bad words to immediately remove from the display after being typed |
|
|
|
|
|
badwords = ["AMK", "lots of bad words"] |
|
|
|
|
|
|
|
|
|
|
|
# use this font to render text |
|
|
|
|
|
fnt = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf', 10) |
|
|
|
|
|
|
|
|
|
|
|
# other variables |
|
|
|
|
|
str1 = "" |
|
|
|
|
|
str2 = "" |
|
|
|
|
|
str3 = "" |
|
|
|
|
|
cnt = 0 |
|
|
|
|
|
letter = "" |
|
|
|
|
|
idle = 0 |
|
|
|
|
|
redraw = False |
|
|
|
|
|
|
|
|
|
|
|
def displayGIF(strip, imageName, wait_ms=500): |
|
|
|
|
|
''' |
|
|
|
|
|
Helper function to render GIF files for the display |
|
|
|
|
|
requires the GIFs to have the same dimensions as the display |
|
|
|
|
|
''' |
|
|
|
|
|
im = Image.open(imageName) |
|
|
|
|
|
width = im.size[0] |
|
|
|
|
|
height = im.size[1] |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
while 1: |
|
|
|
|
|
buf = im.convert('RGB') |
|
|
|
|
|
i = 0 |
|
|
|
|
|
for y in range(0, height): |
|
|
|
|
|
for x in range(0, width): |
|
|
|
|
|
if y%2 != 0: |
|
|
|
|
|
r,g,b = buf.getpixel((29-x,y)) |
|
|
|
|
|
else: |
|
|
|
|
|
r,g,b = buf.getpixel((x,y)) |
|
|
|
|
|
|
|
|
|
|
|
strip.setPixelColor(i, Color(r, g, b)) |
|
|
|
|
|
i += 1 |
|
|
|
|
|
strip.show() |
|
|
|
|
|
im.seek(im.tell()+1) |
|
|
|
|
|
|
|
|
|
|
|
## this part converts the display contents for the preview window |
|
|
|
|
|
imgString = buf.tobytes() |
|
|
|
|
|
pygame_surface = pygame.image.fromstring(imgString, (30,30), 'RGB') |
|
|
|
|
|
screen.blit(pygame_surface, (10,10)) |
|
|
|
|
|
pygame.display.flip() |
|
|
|
|
|
## end of part |
|
|
|
|
|
|
|
|
|
|
|
time.sleep(im.info["duration"]/1000.0) |
|
|
|
|
|
except EOFError: |
|
|
|
|
|
pass # end of sequence |
|
|
|
|
|
|
|
|
|
|
|
def reset_screen(): |
|
|
|
|
|
''' reset strings to empty ''' |
|
|
|
|
|
global str1,str2,str3 |
|
|
|
|
|
str1 = "" |
|
|
|
|
|
str2 = "" |
|
|
|
|
|
str3 = "" |
|
|
|
|
|
|
|
|
|
|
|
def print_screen(): |
|
|
|
|
|
''' take current image data and display it on the LED strip ''' |
|
|
|
|
|
global im |
|
|
|
|
|
i = 0 |
|
|
|
|
|
for y in range(0, 30): |
|
|
|
|
|
for x in range(0, 30): |
|
|
|
|
|
if y%2 != 0: |
|
|
|
|
|
r,g,b = im.getpixel((29-x,y)) |
|
|
|
|
|
else: |
|
|
|
|
|
r,g,b = im.getpixel((x,y)) |
|
|
|
|
|
strip.setPixelColor(i, Color(r,g,b)) |
|
|
|
|
|
i += 1 |
|
|
|
|
|
imgString = im.tobytes() |
|
|
|
|
|
pygame_surface = pygame.image.fromstring(imgString, (30,30), 'RGB') |
|
|
|
|
|
screen.blit(pygame_surface, (10,10)) |
|
|
|
|
|
pygame.display.flip() |
|
|
|
|
|
strip.show() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while True: |
|
|
|
|
|
# create new image every iteration |
|
|
|
|
|
im = Image.new('RGB', (30, 30), color = (0,0,0)) |
|
|
|
|
|
d = ImageDraw.Draw(im) |
|
|
|
|
|
|
|
|
|
|
|
# does the screen need to be reprinted? (relevant for profanity filter) |
|
|
|
|
|
if redraw: |
|
|
|
|
|
redraw = False |
|
|
|
|
|
pygame.time.wait(500) |
|
|
|
|
|
print_screen() |
|
|
|
|
|
|
|
|
|
|
|
for event in pygame.event.get(): |
|
|
|
|
|
# capture keypress events |
|
|
|
|
|
if (event.type == pygame.KEYDOWN): |
|
|
|
|
|
idle = 0 |
|
|
|
|
|
if (event.key == pygame.K_a): |
|
|
|
|
|
letter = "A" |
|
|
|
|
|
if (event.key == pygame.K_b): |
|
|
|
|
|
letter = "B" |
|
|
|
|
|
if (event.key == pygame.K_c): |
|
|
|
|
|
letter = "C" |
|
|
|
|
|
if (event.key == pygame.K_d): |
|
|
|
|
|
letter = "D" |
|
|
|
|
|
if (event.key == pygame.K_e): |
|
|
|
|
|
letter = "E" |
|
|
|
|
|
if (event.key == pygame.K_f): |
|
|
|
|
|
letter = "F" |
|
|
|
|
|
if (event.key == pygame.K_g): |
|
|
|
|
|
letter = "G" |
|
|
|
|
|
if (event.key == pygame.K_h): |
|
|
|
|
|
letter = "H" |
|
|
|
|
|
if (event.key == pygame.K_i): |
|
|
|
|
|
letter = "I" |
|
|
|
|
|
if (event.key == pygame.K_j): |
|
|
|
|
|
letter = "J" |
|
|
|
|
|
if (event.key == pygame.K_k): |
|
|
|
|
|
letter = "K" |
|
|
|
|
|
if (event.key == pygame.K_l): |
|
|
|
|
|
letter = "L" |
|
|
|
|
|
if (event.key == pygame.K_m): |
|
|
|
|
|
letter = "M" |
|
|
|
|
|
if (event.key == pygame.K_n): |
|
|
|
|
|
letter = "N" |
|
|
|
|
|
if (event.key == pygame.K_o): |
|
|
|
|
|
letter = "O" |
|
|
|
|
|
if (event.key == pygame.K_p): |
|
|
|
|
|
letter = "P" |
|
|
|
|
|
if (event.key == pygame.K_q): |
|
|
|
|
|
letter = "Q" |
|
|
|
|
|
if (event.key == pygame.K_r): |
|
|
|
|
|
letter = "R" |
|
|
|
|
|
if (event.key == pygame.K_s): |
|
|
|
|
|
letter = "S" |
|
|
|
|
|
if (event.key == pygame.K_t): |
|
|
|
|
|
letter = "T" |
|
|
|
|
|
if (event.key == pygame.K_u): |
|
|
|
|
|
letter = "U" |
|
|
|
|
|
if (event.key == pygame.K_v): |
|
|
|
|
|
letter = "V" |
|
|
|
|
|
if (event.key == pygame.K_w): |
|
|
|
|
|
letter = "W" |
|
|
|
|
|
if (event.key == pygame.K_x): |
|
|
|
|
|
letter = "X" |
|
|
|
|
|
if (event.key == pygame.K_y): |
|
|
|
|
|
letter = "Z" |
|
|
|
|
|
if (event.key == pygame.K_z): |
|
|
|
|
|
letter = "Y" |
|
|
|
|
|
if (event.key == pygame.K_0): |
|
|
|
|
|
letter = "0" |
|
|
|
|
|
if (event.key == pygame.K_1): |
|
|
|
|
|
letter = "1" |
|
|
|
|
|
if (event.key == pygame.K_2): |
|
|
|
|
|
letter = "2" |
|
|
|
|
|
if (event.key == pygame.K_3): |
|
|
|
|
|
letter = "3" |
|
|
|
|
|
if (event.key == pygame.K_4): |
|
|
|
|
|
letter = "4" |
|
|
|
|
|
if (event.key == pygame.K_5): |
|
|
|
|
|
letter = "5" |
|
|
|
|
|
if (event.key == pygame.K_6): |
|
|
|
|
|
letter = "6" |
|
|
|
|
|
if (event.key == pygame.K_7): |
|
|
|
|
|
letter = "7" |
|
|
|
|
|
if (event.key == pygame.K_8): |
|
|
|
|
|
letter = "8" |
|
|
|
|
|
if (event.key == pygame.K_9): |
|
|
|
|
|
letter = "9" |
|
|
|
|
|
if (event.key == pygame.K_SPACE): |
|
|
|
|
|
letter = " " |
|
|
|
|
|
if (event.key == pygame.K_BACKSPACE): |
|
|
|
|
|
print("backspace") |
|
|
|
|
|
reset_screen() |
|
|
|
|
|
print_screen() |
|
|
|
|
|
cnt = 0 |
|
|
|
|
|
break |
|
|
|
|
|
if (event.key == pygame.K_RETURN): |
|
|
|
|
|
if (cnt < 5): |
|
|
|
|
|
cnt = 4 |
|
|
|
|
|
if (cnt > 5 and cnt < 10): |
|
|
|
|
|
cnt = 9 |
|
|
|
|
|
if (cnt > 10 and cnt <= 15): |
|
|
|
|
|
cnt = -1 |
|
|
|
|
|
str1 = str2 = str3 = "" |
|
|
|
|
|
letter = "" |
|
|
|
|
|
break |
|
|
|
|
|
if (event.key != pygame.K_RETURN and letter == ""): |
|
|
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
if (cnt >= 15): |
|
|
|
|
|
cnt = 0 |
|
|
|
|
|
str1 = "" |
|
|
|
|
|
str2 = "" |
|
|
|
|
|
str3 = "" |
|
|
|
|
|
|
|
|
|
|
|
cnt += 1 |
|
|
|
|
|
|
|
|
|
|
|
if (cnt > 15): |
|
|
|
|
|
cnt = 0 |
|
|
|
|
|
str1 = "" |
|
|
|
|
|
str2 = "" |
|
|
|
|
|
str3 = "" |
|
|
|
|
|
|
|
|
|
|
|
if (event.key == pygame.K_RETURN): |
|
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
if (cnt <= 5): |
|
|
|
|
|
str1 = "{}{}".format(str1, letter) |
|
|
|
|
|
print("Adding letter {} to line 1".format(letter)) |
|
|
|
|
|
if (cnt > 5 and cnt <= 10): |
|
|
|
|
|
str2 = "{}{}".format(str2, letter) |
|
|
|
|
|
print("Adding letter {} to line 2".format(letter)) |
|
|
|
|
|
if (cnt > 10 and cnt <= 15): |
|
|
|
|
|
str3 = "{}{}".format(str3, letter) |
|
|
|
|
|
print("Adding letter {} to line 3".format(letter)) |
|
|
|
|
|
|
|
|
|
|
|
letter = "" |
|
|
|
|
|
d.text((0,0), str1, font=fnt, fill=(255,255,255)) |
|
|
|
|
|
d.text((0,10), str2, font=fnt, fill=(255,255,255)) |
|
|
|
|
|
d.text((0,20), str3, font=fnt, fill=(255,255,255)) |
|
|
|
|
|
print("Line 1: {}".format(str1)) |
|
|
|
|
|
print("Line 2: {}".format(str2)) |
|
|
|
|
|
print("Line 3: {}".format(str3)) |
|
|
|
|
|
print(" ") |
|
|
|
|
|
|
|
|
|
|
|
profanity = "{}{}{}".format(str1,str2,str3) |
|
|
|
|
|
|
|
|
|
|
|
for word in badwords: |
|
|
|
|
|
if (word in profanity): |
|
|
|
|
|
print ("Found a naughty word - resetting! - {}".format(profanity)) |
|
|
|
|
|
reset_screen() |
|
|
|
|
|
cnt = 0 |
|
|
|
|
|
im.paste((255,0,0), [0,0,30,30]) |
|
|
|
|
|
redraw = True |
|
|
|
|
|
|
|
|
|
|
|
# have a couple of easter eggs to surprise visitors if they type this word (abusing the profanity-filter functionality) |
|
|
|
|
|
if ("PONG" in profanity): |
|
|
|
|
|
print("Pong animation") |
|
|
|
|
|
displayGIF(strip, "pong.gif") |
|
|
|
|
|
reset_screen() |
|
|
|
|
|
cnt = 0 |
|
|
|
|
|
print("EOA") |
|
|
|
|
|
|
|
|
|
|
|
if ("BADAPPLE" in profanity): |
|
|
|
|
|
print("Bad Apple Animation") |
|
|
|
|
|
strip.setBrightness(100) |
|
|
|
|
|
displayGIF(strip, "badapple.gif") |
|
|
|
|
|
strip.setBrightness(LED_BRIGHTNESS) |
|
|
|
|
|
reset_screen() |
|
|
|
|
|
cnt = 0 |
|
|
|
|
|
print("EOA") |
|
|
|
|
|
|
|
|
|
|
|
if ("MAKERFAIRE" in profanity): |
|
|
|
|
|
print("MakerFaire Animation") |
|
|
|
|
|
displayGIF(strip, "makerfaire.gif") |
|
|
|
|
|
reset_screen() |
|
|
|
|
|
cnt = 0 |
|
|
|
|
|
print("EOA") |
|
|
|
|
|
|
|
|
|
|
|
if ("BREADBOARD" in profanity): |
|
|
|
|
|
print("Breadboarder Animation") |
|
|
|
|
|
displayGIF(strip, "breadboarder_top.gif") |
|
|
|
|
|
reset_screen() |
|
|
|
|
|
cnt = 0 |
|
|
|
|
|
print("EOA") |
|
|
|
|
|
|
|
|
|
|
|
print_screen() |
|
|
|
|
|
|
|
|
|
|
|
# have an idle animation to attract visitors when nothing's been typed for a while |
|
|
|
|
|
if (idle > 5000): |
|
|
|
|
|
displayGIF(strip, "breadboarder_top.gif") |
|
|
|
|
|
displayGIF(strip, "breadboarder_bottom.gif") |
|
|
|
|
|
displayGIF(strip, "pong.gif") |
|
|
|
|
|
pygame.time.wait(300) |
|
|
|
|
|
displayGIF(strip, "type.gif") |
|
|
|
|
|
idle = -5000 |
|
|
|
|
|
str1 = "TYPE" |
|
|
|
|
|
str2 = "HERE" |
|
|
|
|
|
str3 = " NOW" |
|
|
|
|
|
im = Image.new('RGB', (30, 30), color = (0,0,0)) |
|
|
|
|
|
d = ImageDraw.Draw(im) |
|
|
|
|
|
d.text((0,0), str1, font=fnt, fill=(255,255,255)) |
|
|
|
|
|
d.text((0,10), str2, font=fnt, fill=(255,255,255)) |
|
|
|
|
|
d.text((0,20), str3, font=fnt, fill=(255,255,255)) |
|
|
|
|
|
print_screen() |
|
|
|
|
|
reset_screen() |
|
|
|
|
|
cnt = 0 |
|
|
|
|
|
|
|
|
|
|
|
idle += 1 |
|
|
|
|
|
pygame.time.wait(10) |