''' 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)