commit 11df21ee5126c16b0d48a3468472e472efc5feb8 Author: komaspieler Date: Thu Jun 7 23:29:12 2018 +0200 Maker Faire WS2812 Display Source diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e20908b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "rpi_ws281x"] + path = rpi_ws281x + url = https://github.com/jgarff/rpi_ws281x.git diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/makerfaire-demos/gifs/badapple.gif b/makerfaire-demos/gifs/badapple.gif new file mode 100644 index 0000000..2234329 Binary files /dev/null and b/makerfaire-demos/gifs/badapple.gif differ diff --git a/makerfaire-demos/gifs/breadboarder_bottom.gif b/makerfaire-demos/gifs/breadboarder_bottom.gif new file mode 100644 index 0000000..83875b0 Binary files /dev/null and b/makerfaire-demos/gifs/breadboarder_bottom.gif differ diff --git a/makerfaire-demos/gifs/breadboarder_hue.gif b/makerfaire-demos/gifs/breadboarder_hue.gif new file mode 100644 index 0000000..0f908b1 Binary files /dev/null and b/makerfaire-demos/gifs/breadboarder_hue.gif differ diff --git a/makerfaire-demos/gifs/breadboarder_top.gif b/makerfaire-demos/gifs/breadboarder_top.gif new file mode 100644 index 0000000..1e6c357 Binary files /dev/null and b/makerfaire-demos/gifs/breadboarder_top.gif differ diff --git a/makerfaire-demos/gifs/colorwheel.gif b/makerfaire-demos/gifs/colorwheel.gif new file mode 100644 index 0000000..d88a98e Binary files /dev/null and b/makerfaire-demos/gifs/colorwheel.gif differ diff --git a/makerfaire-demos/gifs/makerfaire.gif b/makerfaire-demos/gifs/makerfaire.gif new file mode 100644 index 0000000..e56a438 Binary files /dev/null and b/makerfaire-demos/gifs/makerfaire.gif differ diff --git a/makerfaire-demos/gifs/makerfaire_hue.gif b/makerfaire-demos/gifs/makerfaire_hue.gif new file mode 100644 index 0000000..5d85861 Binary files /dev/null and b/makerfaire-demos/gifs/makerfaire_hue.gif differ diff --git a/makerfaire-demos/gifs/pong.gif b/makerfaire-demos/gifs/pong.gif new file mode 100644 index 0000000..cd827d0 Binary files /dev/null and b/makerfaire-demos/gifs/pong.gif differ diff --git a/makerfaire-demos/gifs/type.gif b/makerfaire-demos/gifs/type.gif new file mode 100644 index 0000000..964211a Binary files /dev/null and b/makerfaire-demos/gifs/type.gif differ diff --git a/makerfaire-demos/live_typing.py b/makerfaire-demos/live_typing.py new file mode 100644 index 0000000..419e18d --- /dev/null +++ b/makerfaire-demos/live_typing.py @@ -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) diff --git a/makerfaire-demos/static_demo.py b/makerfaire-demos/static_demo.py new file mode 100644 index 0000000..e6cf215 --- /dev/null +++ b/makerfaire-demos/static_demo.py @@ -0,0 +1,224 @@ +# NeoPixel library strandtest example +# Author: Tony DiCola (tony@tonydicola.com) +# +# Direct port of the Arduino NeoPixel library strandtest example. Showcases +# various animations on a strip of NeoPixels. + +# Extended by custom animations for Maker Faire Berlin 2018 + +import time + +from neopixel import * +from PIL import Image, ImageDraw, ImageFont +import signal +import sys + +def signal_handler(signal, frame): + for i in range(0,900): + strip.setPixelColor(i, Color(0,0,0)) + + strip.show() + print("Canceled Animations") + sys.exit(0) + +# LED strip configuration: +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 transistor level shift) +LED_CHANNEL = 0 # set to '1' for GPIOs 13, 19, 41, 45 or 53 +LED_STRIP = ws.WS2811_STRIP_GRB # Strip type and colour ordering + +def displayGIF(strip, imageName, wait_ms=500): + ''' Display a GIF identified by imageName frame by frame ''' + + im = Image.open(imageName) + width = im.size[0] + height = im.size[1] + + # To iterate through the entire gif + 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) + time.sleep(im.info["duration"]/1000.0) + except EOFError: + pass # end of sequence + +def displayJpeg(strip, imName, wait_ms=500): + ''' Display a JPEG file given by imName ''' + img = Image.open(imName) + size = img.size + + buf = img.load() + i = 0 + print("{},{}".format(size[0],size[1])) + print("{}".format(buf[0,0][0])) + for y in range(0, size[1]): + for x in range(0, size[0]): + if y%2 != 0: + strip.setPixelColor(i, Color(buf[29-x,y][0], buf[29-x,y][1], buf[29-x,y][2])) + else: + strip.setPixelColor(i, Color(buf[x,y][0], buf[x,y][1], buf[x,y][2])) + i += 1 + strip.show() + time.sleep(5); + +def pixelTest(strip): + ''' Flash the three base colors to quickly assess LED functionality ''' + + strip.setBrightness(128) # we had to adjust brightness due to an underpowered power supply + + for i in range(strip.numPixels()): + strip.setPixelColor(i, Color(255,0,0)) + strip.show() + time.sleep(1) + for i in range(strip.numPixels()): + strip.setPixelColor(i, Color(0,255,0)) + strip.show() + time.sleep(1) + for i in range(strip.numPixels()): + strip.setPixelColor(i, Color(0,0,255)) + strip.show() + time.sleep(1) + for i in range(strip.numPixels()): + strip.setPixelColor(i, Color(255,255,255)) + strip.show() + time.sleep(1) + + strip.setBrightness(LED_BRIGHTNESS) # restore LED_BRIGHTNESS setting + + + +def theaterChase(strip, color, wait_ms=25, iterations=10): + ''' This function was predefined as part of the official python wrapper demo ''' + """Movie theater light style chaser animation.""" + for j in range(iterations): + for q in range(3): + for i in range(0, strip.numPixels(), 3): + strip.setPixelColor(i+q, color) + strip.show() + time.sleep(wait_ms/1000.0) + for i in range(0, strip.numPixels(), 3): + strip.setPixelColor(i+q, 0) + + +def wheel(pos): + ''' This function was predefined as part of the official python wrapper demo ''' + """Generate rainbow colors across 0-255 positions.""" + if pos < 85: + return Color(pos * 3, 255 - pos * 3, 0) + elif pos < 170: + pos -= 85 + return Color(255 - pos * 3, 0, pos * 3) + else: + pos -= 170 + return Color(0, pos * 3, 255 - pos * 3) + + +def rainbowCycle(strip, wait_ms=20, iterations=5): + ''' This function was predefined as part of the official python wrapper demo ''' + """Draw rainbow that uniformly distributes itself across all pixels.""" + for j in range(256*iterations): + for i in range(strip.numPixels()): + strip.setPixelColor(i, wheel((int(i * 256 / strip.numPixels()) + j) & 255)) + strip.show() + time.sleep(wait_ms/1000.0) + + + +def dynamic(strip): + ''' This was my first attempt at rendering raw text on the display ''' + + im = Image.new('RGB', (30, 30), color = (0,0,0)) + fnt = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf', 10) + d = ImageDraw.Draw(im) + d.text((0,0), "abcde", font=fnt, fill=(255,255,255)) + d.text((0,10), "fgehi", font=fnt, fill=(255,255,250)) + d.text((0,20), "jklmn", font=fnt, fill=(255,250,250)) + 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 + strip.show() + time.sleep(5); + + +# Main program logic follows: +if __name__ == '__main__': + + # Create NeoPixel object with appropriate configuration. + strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL, LED_STRIP) + + # Intialize the library (must be called once before other functions). + strip.begin() + + print ('Press Ctrl-C to quit.') + + while True: + print ('Pixel Test') + pixelTest(strip) + + print ('Breadb top') + displayGIF(strip, "breadboarder_top.gif") + strip.setBrightness(128) + + print ('White Theater') + theaterChase(strip, Color(127, 127, 127), 60) # White theater chase + strip.setBrightness(LED_BRIGHTNESS) + + print ('Makerfaire') + displayGIF(strip, "makerfaire.gif") + + + print ('Red theater') + strip.setBrightness(128) + theaterChase(strip, Color(127, 0, 0), 60) # Red theater chase + strip.setBrightness(LED_BRIGHTNESS) + + print ('Makerfaire Rainbow') + displayGIF(strip, "makerfaire_hue.gif") + + + print ('Blue Theater') + strip.setBrightness(128) + theaterChase(strip, Color( 0, 0, 127), 60) # Blue theater chase + strip.setBrightness(LED_BRIGHTNESS) + + print ('Breadboarder bottom') + displayGIF(strip, "breadboarder_bottom.gif") + + + print ('Rainbowcycle') + strip.setBrightness(128) + rainbowCycle(strip, 1, 1) + strip.setBrightness(LED_BRIGHTNESS) + + print ('Breadboarder rainbow') + displayGIF(strip, "breadboarder_hue.gif") + + print ('Color wheel') + for i in range(0,3): + displayGIF(strip, "colorwheel.gif") + + displayGIF(strip, "pong.gif") diff --git a/rpi_ws281x b/rpi_ws281x new file mode 160000 index 0000000..e4a05d6 --- /dev/null +++ b/rpi_ws281x @@ -0,0 +1 @@ +Subproject commit e4a05d6538c02bb9714f2efc6630f2bfdcf35bf6