commit 4bebb2e955333a0d5188d1cceb643f5b98c837ec
parent a8b2ba411221193108d12dd40908322cfedc1879
Author: Gerd Beuster <gerd@frombelow.net>
Date: Thu, 2 Apr 2020 22:55:38 +0200
Added flask server and RGB mode
Diffstat:
A | blue_green_flames.py | | | 118 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | cellular_automaton.py | | | 95 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | main.py | | | 127 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | patterns.py | | | 355 | ++++++++----------------------------------------------------------------------- |
A | templates/index.html | | | 62 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 436 insertions(+), 321 deletions(-)
diff --git a/blue_green_flames.py b/blue_green_flames.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python3
+
+import time
+from rpi_ws281x import Color
+import random
+import sys
+import PyIgnition, pygame, os
+import subprocess
+import math
+
+import patterns
+
+class BlueGreenFlames(patterns.Pattern):
+ def __init__(self, strip, led_matrix_width, led_matrix_height, led_strip_snake, opts=None):
+ patterns.Pattern.__init__(self, strip=strip, led_matrix_width=led_matrix_width, led_matrix_height=led_matrix_height, led_strip_snake = led_strip_snake, opts=opts)
+ # This mode works great with LED_BRIGHTNESS set to 16.
+ self.SCALE=40 # Scaling factor for screen
+
+ # Also show in window?
+ # self.HEADLESS=False
+ self.HEADLESS=True
+
+ if self.HEADLESS:
+ # On Raspbian, setting a dummy videodriver
+ # does not prevent pygame from trying to
+ # open a X display. Therefore we use a
+ # virtual framebuffer.
+ os.environ["SDL_VIDEODRIVER"] = "dummy"
+ subprocess.Popen(["Xvfb", ":1", "-screen", "0", "1024x768x24"],
+ stderr=subprocess.DEVNULL)
+ time.sleep(1)
+ os.environ["DISPLAY"] = ":1"
+
+ pygame.display.init()
+ self.screen = pygame.Surface((math.ceil(self.led_matrix_width),
+ math.ceil(self.led_matrix_height)))
+ self.screen_scaled = pygame.display.set_mode((math.ceil(self.led_matrix_width*self.SCALE),
+ math.ceil(self.led_matrix_height*self.SCALE)))
+ pygame.display.set_caption("Blue Green Fire")
+ self.clock = pygame.time.Clock()
+
+ self.fire = PyIgnition.ParticleEffect(self.screen, (0, 0),
+ (math.ceil(self.led_matrix_width),
+ math.ceil(self.led_matrix_height)))
+ gravity = self.fire.CreateDirectedGravity(strength=0.1, direction=[0, -1])
+
+ # Behavior of blue particles
+ self.blue_flames = []
+ blue_flames_scale_factor = 4
+ for x_pos in range(0, math.ceil(self.led_matrix_width),
+ math.ceil(self.led_matrix_width//5)):
+ new_flame = self.fire.CreateSource(pos=(x_pos, self.led_matrix_height*1.1),
+ initspeed = .01, initdirection = 0.0,
+ initspeedrandrange = .1,
+ initdirectionrandrange = 0.5,
+ particlesperframe = 1,
+ particlelife = 100,
+ drawtype = PyIgnition.DRAWTYPE_CIRCLE,
+ colour = (50, 20, 200),
+ radius = self.led_matrix_width/32.0*blue_flames_scale_factor)
+ new_flame.CreateParticleKeyframe(1, colour = (50, 20, 200),
+ radius = self.led_matrix_width/32.0*blue_flames_scale_factor)
+ new_flame.CreateParticleKeyframe(3, colour = (0, 0, 150),
+ radius = self.led_matrix_width/32.0*blue_flames_scale_factor)
+ new_flame.CreateParticleKeyframe(6, colour = (20, 20, 50),
+ radius = self.led_matrix_width/32.0*blue_flames_scale_factor)
+ new_flame.CreateParticleKeyframe(9, colour = (0, 0, 0),
+ radius = self.led_matrix_width/32.0*blue_flames_scale_factor)
+ self.blue_flames += [new_flame]
+
+ # Behavior of green particle
+ self.green_flame_x = self.led_matrix_width / 2
+ self.green_flame_y = self.led_matrix_height + 1
+ self.green_flame = self.fire.CreateSource(pos=(self.green_flame_x, self.green_flame_y),
+ initspeed = .3, initdirection = 0.0,
+ initspeedrandrange = .3,
+ initdirectionrandrange = 0.5,
+ particlesperframe = 1, particlelife = 100,
+ drawtype = PyIgnition.DRAWTYPE_CIRCLE,
+ colour = (255, 200, 100),
+ radius = self.led_matrix_width/36.0)
+ self.green_flame.CreateParticleKeyframe(5, colour = (50, 200, 20),
+ radius = self.led_matrix_width/36.0)
+ self.green_flame.CreateParticleKeyframe(10, colour = (0, 150, 0),
+ radius = self.led_matrix_width/36.0)
+ self.green_flame.CreateParticleKeyframe(15, colour = (20, 50, 20),
+ radius = self.led_matrix_width/36.0)
+ self.green_flame.CreateParticleKeyframe(20, colour = (0, 0, 0),
+ radius = self.led_matrix_width/36.0)
+
+ def step(self):
+ # Draw on screen
+ self.screen.fill((0, 0, 0))
+ self.green_flame_x = random.randint(0, math.ceil(self.led_matrix_width)-1)
+ self.green_flame.pos = (self.green_flame_x, self.green_flame_y)
+ if self.green_flame.curframe % 10 == 0:
+ self.green_flame.ConsolidateKeyframes()
+ for b in self.blue_flames:
+ if b.curframe % 30 == 0:
+ b.ConsolidateKeyframes()
+ self.fire.Update()
+ self.fire.Redraw()
+ pygame.transform.scale(self.screen, (math.ceil(self.led_matrix_width*self.SCALE),
+ math.ceil(self.led_matrix_height*self.SCALE)),
+ self.screen_scaled)
+ pygame.display.update()
+ # Draw on LED matrix
+ fb = pygame.PixelArray(self.screen)
+ for y in range(math.ceil(self.led_matrix_height)):
+ for x in range(math.ceil(self.led_matrix_width)):
+ r = fb[x][y] >> 16 & 0xFF
+ g = fb[x][y] >> 8 & 0xFF
+ b = fb[x][y] & 0xFF
+ self.strip.setPixelColor(self.xy2i(x,
+ math.ceil(self.led_matrix_height)-1-y),
+ Color(r, g, b))
+ self.strip.show()
+ self.clock.tick(5)
diff --git a/cellular_automaton.py b/cellular_automaton.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+
+import math
+import random
+from rpi_ws281x import Color
+import time
+
+import patterns
+
+class CellularAutomaton(patterns.Pattern):
+
+ CELL_ON_COLOR = (0x00, 0xFF, 0x00)
+ CELL_OFF_COLOR = (0x00, 0x00, 0x00)
+
+ def cell_is_on(self, buf, x, y):
+ x = (x + self.led_matrix_width) % self.led_matrix_width
+ y = (y + self.led_matrix_height) % self.led_matrix_height
+ return (buf[self.xy2i(x, y)] == self.CELL_ON_COLOR)
+
+
+ def show_buf(self, buf):
+ for i in range(len(buf)):
+ if(buf[i]):
+ self.strip.setPixelColor(i, Color(buf[i][0], buf[i][1], buf[i][2]))
+ else:
+ self.strip.setPixelColor(i, Color(0x00, 0x00, 0x00))
+ self.strip.show()
+
+
+ def __init__(self, strip, led_matrix_width, led_matrix_height, led_strip_snake, opts=None):
+ patterns.Pattern.__init__(self, strip=strip, led_matrix_width=led_matrix_width, led_matrix_height=led_matrix_height, led_strip_snake = led_strip_snake, opts=opts)
+ self.matrix_buf = [[None] * (math.ceil(self.led_matrix_width) * math.ceil(self.led_matrix_height)),
+ [None] * (math.ceil(self.led_matrix_width) * math.ceil(self.led_matrix_height))]
+ self.number_of_updates = 0
+ self.stable_pattern = True
+ self.current_buf = self.matrix_buf[0]
+ for index in range(math.ceil(self.led_matrix_width) * math.ceil(self.led_matrix_height)):
+ self.current_buf[index] = self.CELL_OFF_COLOR
+
+
+ def step(self):
+ self.current_buf = self.matrix_buf[self.number_of_updates % 2]
+ self.next_buf = self.matrix_buf[(self.number_of_updates + 1) % 2]
+ # Since we get a stable pattern after sometime, we restart with a
+ # new random cellular automaton after a fixed number of steps or
+ # when we detect a stable situation.
+ if self.stable_pattern or (self.number_of_updates == 100):
+ if True:
+ # Initialize with random values
+ for index in range(math.ceil(self.led_matrix_width) * math.ceil(self.led_matrix_height)):
+ if (random.random() < .5):
+ self.current_buf[index] = self.CELL_ON_COLOR
+ else:
+ self.current_buf[index] = self.CELL_OFF_COLOR
+ else:
+ # Start with a glider
+ self.current_buf[self.xy2i(3,6)] = self.CELL_ON_COLOR
+ self.current_buf[self.xy2i(4,6)] = self.CELL_ON_COLOR
+ self.current_buf[self.xy2i(5,6)] = self.CELL_ON_COLOR
+ self.current_buf[self.xy2i(5,7)] = self.CELL_ON_COLOR
+ self.current_buf[self.xy2i(4,8)] = self.CELL_ON_COLOR
+ self.number_of_updates = 0
+ self.stable_pattern = False
+
+ self.number_of_updates += 1
+ # Cycle over all cells
+ for y in range(math.ceil(self.led_matrix_height)):
+ for x in range(math.ceil(self.led_matrix_width)):
+ # Count neighbors
+ neighbors = 0
+ for y_neighbor in range(3):
+ for x_neighbor in range(3):
+ if (((x_neighbor != 1) or (y_neighbor != 1)) and
+ self.cell_is_on(self.current_buf,
+ (x-x_neighbor+1) % self.led_matrix_width,
+ (y-y_neighbor+1) % self.led_matrix_height)):
+ neighbors += 1
+ # Update cell
+ if (neighbors < 2) or (neighbors > 3):
+ self.next_buf[self.xy2i(x, y)] = self.CELL_OFF_COLOR
+ elif neighbors == 3:
+ self.next_buf[self.xy2i(x, y)] = self.CELL_ON_COLOR
+ else:
+ self.next_buf[self.xy2i(x, y)] = self.current_buf[self.xy2i(x, y)]
+ # Check if pattern has changed since last update.
+ # (We will exit the loop if the pattern becomes stable)
+ self.stable_pattern = True
+ for i in range(math.ceil(self.led_matrix_height) * math.ceil(self.led_matrix_width)):
+ if self.next_buf[i] != self.current_buf[i]:
+ self.stable_pattern = False
+ break
+ self.show_buf(self.current_buf)
+ time.sleep(.1)
+
+
diff --git a/main.py b/main.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python3
+
+from multiprocessing import Queue, Process
+from flask import Flask, render_template, request
+from rpi_ws281x import PixelStrip, Color
+import math
+import pdb
+
+# Patterns
+import patterns
+import cellular_automaton
+import blue_green_flames
+
+# LED strip/matrix configuration
+
+# Matrix
+#LED_MATRIX_WIDTH = 16
+#LED_MATRIX_HEIGHT = 16
+# Is row layout of LEDs is left-right, right-left, left-right, ...
+# or left-right, left-right, left-right, ...?
+#LED_STRIP_SNAKE = True
+
+# Strip
+#LED_MATRIX_WIDTH = 299
+#LED_MATRIX_HEIGHT = 1
+#LED_STRIP_SNAKE = False
+
+# Pipe with 5 m LED strip, density 60 LEDs / m
+#LED_MATRIX_WIDTH = 14.5
+#LED_MATRIX_HEIGHT = 21
+#LED_STRIP_SNAKE = False
+
+# Pipe with 5 m LED strip, density 144 LEDs / m
+LED_MATRIX_WIDTH = 34.7
+LED_MATRIX_HEIGHT = 21
+LED_STRIP_SNAKE = False
+
+# LED_PIN = 18 # GPIO pin connected to the pixels (18 uses PWM!) - requires root!
+LED_PIN = 10 # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0) - user must be in group gpio!
+LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz)
+LED_DMA = 10 # DMA channel to use for generating signal (try 10)
+# Set to 0 for darkest and 255 for brightest
+# LED_BRIGHTNESS = 255
+LED_BRIGHTNESS = 16
+# LED_BRIGHTNESS = 8
+# LED_BRIGHTNESS = 4
+# LED_BRIGHTNESS = 1
+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
+
+
+
+# Pattern display
+
+# The mode is passed to the run function via a queue. Modes are passed
+# as lists, where the first element is the mode number and subsquent
+# numbers can be used to pass additional information for a specific
+# mode.
+
+MODE_RGB = 0
+MODE_WORM = 1
+MODE_CELLULAR_AUTOMATON = 2
+MODE_BLUE_GREEN_FLAMES = 3
+MODE_TEST_PATTERN = 4
+
+mode = [MODE_BLUE_GREEN_FLAMES]
+# mode = [MODE_WORM]
+# mode = [MODE_CELLULAR_AUTOMATON]
+
+def run(cmd_queue=None):
+ global mode
+ # Create NeoPixel object with appropriate configuration.
+ strip = PixelStrip(math.ceil(LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT), LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
+ # Intialize the library (must be called once before other functions).
+ strip.begin()
+ # This will be immediately replaced by the actual initial mode.
+ p = patterns.Pattern(strip, LED_MATRIX_WIDTH, LED_MATRIX_HEIGHT, LED_STRIP_SNAKE)
+ current_mode = [-1]
+ try:
+ while True:
+ while cmd_queue and (not cmd_queue.empty()):
+ mode = cmd_queue.get_nowait()
+ if current_mode != mode:
+ # External mode switch
+ p.stop()
+ current_mode = mode
+ p = {
+ MODE_RGB : patterns.RGB,
+ MODE_WORM : patterns.Worm,
+ MODE_CELLULAR_AUTOMATON : cellular_automaton.CellularAutomaton,
+ MODE_BLUE_GREEN_FLAMES : blue_green_flames.BlueGreenFlames,
+ MODE_TEST_PATTERN : patterns.TestPattern
+ }[current_mode[0]](strip, LED_MATRIX_WIDTH, LED_MATRIX_HEIGHT, LED_STRIP_SNAKE, current_mode[1:])
+ p.step()
+ except KeyboardInterrupt:
+ p.clear_strip()
+ p.stop()
+
+# Run patterns in separate process
+cmd_queue = Queue()
+run = Process(target=run, args=(cmd_queue,))
+run.start()
+
+# Run webserver in this process
+app = Flask(__name__)
+
+@app.route('/', methods=['GET', 'POST'])
+@app.route('/index.php', methods=['GET', 'POST'])
+def hello_world():
+ # In general, we accept both GET and POST request,
+ # but for technical reasons, RGB values come only as GET requests.
+ (r, g, b) = (int(request.args.get('red', '0')),
+ int(request.args.get('green', '0')),
+ int(request.args.get('blue', '0')))
+ mode = request.args.get('mode', None)
+ if not mode:
+ mode = request.form.get('mode', None)
+ if mode != None:
+ mode_number = {'rgb': [MODE_RGB, r, g, b],
+ 'worm': [MODE_WORM],
+ 'cellular': [MODE_CELLULAR_AUTOMATON],
+ 'flames': [MODE_BLUE_GREEN_FLAMES],
+ 'test': [MODE_TEST_PATTERN]
+ }[mode]
+ cmd_queue.put(mode_number)
+ # pdb.set_trace()
+ return render_template('index.html')
diff --git a/patterns.py b/patterns.py
@@ -6,86 +6,52 @@
import time
from rpi_ws281x import PixelStrip, Color
-import argparse
import random
import sys
sys.path.append('pyignition')
-import PyIgnition, pygame, sys, os
+import PyIgnition, pygame, os
import subprocess
import math
import pdb
-import multiprocessing
-
-# LED strip/matrix configuration
-
-# Matrix
-#LED_MATRIX_WIDTH = 16
-#LED_MATRIX_HEIGHT = 16
-# Is row layout of LEDs is left-right, right-left, left-right, ...
-# or left-right, left-right, left-right, ...?
-#LED_STRIP_SNAKE = True
-
-# Strip
-#LED_MATRIX_WIDTH = 299
-#LED_MATRIX_HEIGHT = 1
-#LED_STRIP_SNAKE = False
-
-# Pipe with 5 m LED strip, density 60 LEDs / m
-#LED_MATRIX_WIDTH = 14.5
-#LED_MATRIX_HEIGHT = 21
-#LED_STRIP_SNAKE = False
-
-# Pipe with 5 m LED strip, density 144 LEDs / m
-LED_MATRIX_WIDTH = 34.7
-LED_MATRIX_HEIGHT = 21
-LED_STRIP_SNAKE = False
-
-# LED_PIN = 18 # GPIO pin connected to the pixels (18 uses PWM!) - requires root!
-LED_PIN = 10 # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0) - user must be in group gpio!
-LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz)
-LED_DMA = 10 # DMA channel to use for generating signal (try 10)
-# Set to 0 for darkest and 255 for brightest
-# LED_BRIGHTNESS = 255
-LED_BRIGHTNESS = 16
-# LED_BRIGHTNESS = 8
-# LED_BRIGHTNESS = 4
-# LED_BRIGHTNESS = 1
-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
class Pattern():
- def __init__(self, strip):
+ def __init__(self, strip, led_matrix_width, led_matrix_height, led_strip_snake, opts=None):
self.strip = strip
- self.clear_strip()
+ self.led_matrix_width = led_matrix_width
+ self.led_matrix_height = led_matrix_height
+ self.led_strip_snake = led_strip_snake
+ self.opts = opts
+ # self.clear_strip()
def step(self):
pass
def stop(self):
- self.clear_strip()
+ pass
def clear_strip(self):
- for i in range(math.ceil(LED_MATRIX_WIDTH) * math.ceil(LED_MATRIX_HEIGHT)):
+ for i in range(math.ceil(self.led_matrix_width) * math.ceil(self.led_matrix_height)):
self.strip.setPixelColor(i, Color(0x00, 0x00, 0x00))
self.strip.show()
def xy2i(self, x, y):
- i = y * LED_MATRIX_WIDTH
- if LED_STRIP_SNAKE and (y % 2):
- i += LED_MATRIX_WIDTH - x -1
+ i = y * self.led_matrix_width
+ if self.led_strip_snake and (y % 2):
+ i += self.led_matrix_width - x -1
else:
i += x
return math.floor(i)
+
class TestPattern(Pattern):
CELL_ON = Color(0x00, 0xFF, 0x00)
CELL_OFF = Color(0x00, 0x00, 0x00)
- def __init__(self, strip):
- Pattern.__init__(self, strip=strip)
+ def __init__(self, strip, led_matrix_width, led_matrix_height, led_strip_snake, opts=None):
+ Pattern.__init__(self, strip=strip, led_matrix_width=led_matrix_width, led_matrix_height=led_matrix_height, led_strip_snake = led_strip_snake, opts=opts)
# Initialize dummy display
os.environ["SDL_VIDEODRIVER"] = "dummy"
subprocess.Popen(["Xvfb", ":1", "-screen", "0", "1024x768x24"],
@@ -99,29 +65,32 @@ class TestPattern(Pattern):
def step(self):
print("({x}/{y})".format(x=self.x, y=self.y))
- for i in range(math.ceil(LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT)):
+ for i in range(math.ceil(self.led_matrix_width * self.led_matrix_height)):
self.strip.setPixelColor(i, self.CELL_OFF)
- for col in range(math.ceil(LED_MATRIX_WIDTH)):
+ for col in range(math.ceil(self.led_matrix_width)):
self.strip.setPixelColor(self.xy2i(col, self.y), self.CELL_ON)
- for row in range(math.ceil(LED_MATRIX_HEIGHT)):
+ for row in range(math.ceil(self.led_matrix_height)):
self.strip.setPixelColor(self.xy2i(self.x, row), self.CELL_ON)
self.strip.show()
time.sleep(1)
- self.x = (self.x + 1) % LED_MATRIX_WIDTH
- self.y = (self.y + 1) % LED_MATRIX_HEIGHT
+ self.x = (self.x + 1) % self.led_matrix_width
+ self.y = (self.y + 1) % self.led_matrix_height
+
-class AllOn(Pattern):
- def __init__(self, strip):
- Pattern.__init__(self, strip=strip)
- for i in range(math.ceil(LED_MATRIX_WIDTH*LED_MATRIX_HEIGHT)):
- strip.setPixelColor(i, Color(LED_BRIGHTNESS, LED_BRIGHTNESS, LED_BRIGHTNESS))
+class RGB(Pattern):
+ def __init__(self, strip, led_matrix_width, led_matrix_height, led_strip_snake, opts=None):
+ Pattern.__init__(self, strip=strip, led_matrix_width=led_matrix_width, led_matrix_height=led_matrix_height, led_strip_snake = led_strip_snake, opts=opts)
+ (r, g, b) = opts
+ for i in range(math.ceil(self.led_matrix_width*self.led_matrix_height)):
+ strip.setPixelColor(i, Color(r, g, b))
strip.show()
+
class Worm(Pattern):
- def __init__(self, strip):
- Pattern.__init__(self, strip=strip)
+ def __init__(self, strip, led_matrix_width, led_matrix_height, led_strip_snake, opts=None):
+ Pattern.__init__(self, strip=strip, led_matrix_width=led_matrix_width, led_matrix_height=led_matrix_height, led_strip_snake = led_strip_snake, opts=opts)
# LED_BRIGHTNESS should be at least 15.
self.worm_pattern = list(map(lambda x: Color(x[0], x[1], x[2]),
[(0x00, 0x05, 0x00),
@@ -137,11 +106,11 @@ class Worm(Pattern):
(0x00, 0x05, 0x00),
]))
# Set background color
- for i in range(math.ceil(LED_MATRIX_WIDTH*LED_MATRIX_HEIGHT)):
+ for i in range(math.ceil(self.led_matrix_width*self.led_matrix_height)):
self.strip.setPixelColor(i, Color(0x00, 0x05, 0x00))
self.strip.show()
self.direction = 1
- self.pos = LED_MATRIX_WIDTH // 2
+ self.pos = self.led_matrix_width // 2
def step(self):
# Larson worm
@@ -151,271 +120,15 @@ class Worm(Pattern):
self.direction *= -1
if self.pos == 0:
self.direction = 1
- if (self.pos + len(self.worm_pattern)) == LED_MATRIX_WIDTH*LED_MATRIX_HEIGHT:
+ if (self.pos + len(self.worm_pattern)) == self.led_matrix_width*self.led_matrix_height:
self.direction = -1
self.pos += self.direction
# Random set blue pixel
if (self.pos % 8) == 0:
- self.strip.setPixelColor(random.randint(0, math.ceil(LED_MATRIX_WIDTH*LED_MATRIX_HEIGHT-1)), Color(0x00, 0x00, 0x10))
+ self.strip.setPixelColor(random.randint(0, math.ceil(self.led_matrix_width*self.led_matrix_height-1)), Color(0x00, 0x00, 0x10))
# Update Strip
self.strip.show()
time.sleep(.04)
-class CellularAutomaton(Pattern):
-
- CELL_ON_COLOR = (0x00, 0xFF, 0x00)
- CELL_OFF_COLOR = (0x00, 0x00, 0x00)
-
- def cell_is_on(self, buf, x, y):
- x = (x + LED_MATRIX_WIDTH) % LED_MATRIX_WIDTH
- y = (y + LED_MATRIX_HEIGHT) % LED_MATRIX_HEIGHT
- return (buf[self.xy2i(x, y)] == self.CELL_ON_COLOR)
-
-
- def show_buf(self, buf):
- for i in range(len(buf)):
- if(buf[i]):
- self.strip.setPixelColor(i, Color(buf[i][0], buf[i][1], buf[i][2]))
- else:
- self.strip.setPixelColor(i, Color(0x00, 0x00, 0x00))
- self.strip.show()
-
-
- def __init__(self, strip):
- Pattern.__init__(self, strip=strip)
- self.matrix_buf = [[None] * (math.ceil(LED_MATRIX_WIDTH) * math.ceil(LED_MATRIX_HEIGHT)),
- [None] * (math.ceil(LED_MATRIX_WIDTH) * math.ceil(LED_MATRIX_HEIGHT))]
- self.number_of_updates = 0
- self.stable_pattern = True
- self.current_buf = self.matrix_buf[0]
- for index in range(math.ceil(LED_MATRIX_WIDTH) * math.ceil(LED_MATRIX_HEIGHT)):
- self.current_buf[index] = self.CELL_OFF_COLOR
-
-
- def step(self):
- self.current_buf = self.matrix_buf[self.number_of_updates % 2]
- self.next_buf = self.matrix_buf[(self.number_of_updates + 1) % 2]
- # Since we get a stable pattern after sometime, we restart with a
- # new random cellular automaton after a fixed number of steps or
- # when we detect a stable situation.
- if self.stable_pattern or (self.number_of_updates == 100):
- if True:
- # Initialize with random values
- for index in range(math.ceil(LED_MATRIX_WIDTH) * math.ceil(LED_MATRIX_HEIGHT)):
- if (random.random() < .5):
- self.current_buf[index] = self.CELL_ON_COLOR
- else:
- self.current_buf[index] = self.CELL_OFF_COLOR
- else:
- # Start with a glider
- self.current_buf[self.xy2i(3,6)] = self.CELL_ON_COLOR
- self.current_buf[self.xy2i(4,6)] = self.CELL_ON_COLOR
- self.current_buf[self.xy2i(5,6)] = self.CELL_ON_COLOR
- self.current_buf[self.xy2i(5,7)] = self.CELL_ON_COLOR
- self.current_buf[self.xy2i(4,8)] = self.CELL_ON_COLOR
- self.number_of_updates = 0
- self.stable_pattern = False
-
- self.number_of_updates += 1
- # Cycle over all cells
- for y in range(math.ceil(LED_MATRIX_HEIGHT)):
- for x in range(math.ceil(LED_MATRIX_WIDTH)):
- # Count neighbors
- neighbors = 0
- for y_neighbor in range(3):
- for x_neighbor in range(3):
- if (((x_neighbor != 1) or (y_neighbor != 1)) and
- self.cell_is_on(self.current_buf,
- (x-x_neighbor+1) % LED_MATRIX_WIDTH,
- (y-y_neighbor+1) % LED_MATRIX_HEIGHT)):
- neighbors += 1
- # Update cell
- if (neighbors < 2) or (neighbors > 3):
- self.next_buf[self.xy2i(x, y)] = self.CELL_OFF_COLOR
- elif neighbors == 3:
- self.next_buf[self.xy2i(x, y)] = self.CELL_ON_COLOR
- else:
- self.next_buf[self.xy2i(x, y)] = self.current_buf[self.xy2i(x, y)]
- # Check if pattern has changed since last update.
- # (We will exit the loop if the pattern becomes stable)
- self.stable_pattern = True
- for i in range(math.ceil(LED_MATRIX_HEIGHT) * math.ceil(LED_MATRIX_WIDTH)):
- if self.next_buf[i] != self.current_buf[i]:
- self.stable_pattern = False
- break
- self.show_buf(self.current_buf)
- time.sleep(.1)
-
-
-class BlueGreenFlames(Pattern):
- def __init__(self, strip):
- Pattern.__init__(self, strip=strip)
- # This mode works great with LED_BRIGHTNESS set to 16.
- self.SCALE=40 # Scaling factor for screen
-
- # Also show in window?
- # self.HEADLESS=False
- self.HEADLESS=True
-
- if self.HEADLESS:
- # On Raspbian, setting a dummy videodriver
- # does not prevent pygame from trying to
- # open a X display. Therefore we use a
- # virtual framebuffer.
- os.environ["SDL_VIDEODRIVER"] = "dummy"
- subprocess.Popen(["Xvfb", ":1", "-screen", "0", "1024x768x24"],
- stderr=subprocess.DEVNULL)
- time.sleep(1)
- os.environ["DISPLAY"] = ":1"
-
- pygame.display.init()
- self.screen = pygame.Surface((math.ceil(LED_MATRIX_WIDTH),
- math.ceil(LED_MATRIX_HEIGHT)))
- self.screen_scaled = pygame.display.set_mode((math.ceil(LED_MATRIX_WIDTH*self.SCALE),
- math.ceil(LED_MATRIX_HEIGHT*self.SCALE)))
- pygame.display.set_caption("Blue Green Fire")
- self.clock = pygame.time.Clock()
-
- self.fire = PyIgnition.ParticleEffect(self.screen, (0, 0),
- (math.ceil(LED_MATRIX_WIDTH),
- math.ceil(LED_MATRIX_HEIGHT)))
- gravity = self.fire.CreateDirectedGravity(strength=0.1, direction=[0, -1])
-
- # Behavior of blue particles
- self.blue_flames = []
- blue_flames_scale_factor = 4
- for x_pos in range(0, math.ceil(LED_MATRIX_WIDTH),
- math.ceil(LED_MATRIX_WIDTH//5)):
- new_flame = self.fire.CreateSource(pos=(x_pos, LED_MATRIX_HEIGHT*1.1),
- initspeed = .01, initdirection = 0.0,
- initspeedrandrange = .1,
- initdirectionrandrange = 0.5,
- particlesperframe = 1,
- particlelife = 100,
- drawtype = PyIgnition.DRAWTYPE_CIRCLE,
- colour = (50, 20, 200),
- radius = LED_MATRIX_WIDTH/32.0*blue_flames_scale_factor)
- new_flame.CreateParticleKeyframe(1, colour = (50, 20, 200),
- radius = LED_MATRIX_WIDTH/32.0*blue_flames_scale_factor)
- new_flame.CreateParticleKeyframe(3, colour = (0, 0, 150),
- radius = LED_MATRIX_WIDTH/32.0*blue_flames_scale_factor)
- new_flame.CreateParticleKeyframe(6, colour = (20, 20, 50),
- radius = LED_MATRIX_WIDTH/32.0*blue_flames_scale_factor)
- new_flame.CreateParticleKeyframe(9, colour = (0, 0, 0),
- radius = LED_MATRIX_WIDTH/32.0*blue_flames_scale_factor)
- self.blue_flames += [new_flame]
-
-
- # Behavior of green particle
- self.green_flame_x = LED_MATRIX_WIDTH / 2
- self.green_flame_y = LED_MATRIX_HEIGHT + 1
- self.green_flame = self.fire.CreateSource(pos=(self.green_flame_x, self.green_flame_y),
- initspeed = .3, initdirection = 0.0,
- initspeedrandrange = .3,
- initdirectionrandrange = 0.5,
- particlesperframe = 1, particlelife = 100,
- drawtype = PyIgnition.DRAWTYPE_CIRCLE,
- colour = (255, 200, 100),
- radius = LED_MATRIX_WIDTH/36.0)
- self.green_flame.CreateParticleKeyframe(5, colour = (50, 200, 20),
- radius = LED_MATRIX_WIDTH/36.0)
- self.green_flame.CreateParticleKeyframe(10, colour = (0, 150, 0),
- radius = LED_MATRIX_WIDTH/36.0)
- self.green_flame.CreateParticleKeyframe(15, colour = (20, 50, 20),
- radius = LED_MATRIX_WIDTH/36.0)
- self.green_flame.CreateParticleKeyframe(20, colour = (0, 0, 0),
- radius = LED_MATRIX_WIDTH/36.0)
-
-
-
-
- def step(self):
- for event in pygame.event.get():
- if event.type == pygame.QUIT:
- sys.exit()
- # Draw on screen
- self.screen.fill((0, 0, 0))
- self.green_flame_x = random.randint(0, math.ceil(LED_MATRIX_WIDTH)-1)
- self.green_flame.pos = (self.green_flame_x, self.green_flame_y)
- if self.green_flame.curframe % 10 == 0:
- self.green_flame.ConsolidateKeyframes()
- for b in self.blue_flames:
- if b.curframe % 30 == 0:
- b.ConsolidateKeyframes()
- self.fire.Update()
- self.fire.Redraw()
- pygame.transform.scale(self.screen, (math.ceil(LED_MATRIX_WIDTH*self.SCALE),
- math.ceil(LED_MATRIX_HEIGHT*self.SCALE)),
- self.screen_scaled)
- pygame.display.update()
- # Draw on LED matrix
- fb = pygame.PixelArray(self.screen)
- for y in range(math.ceil(LED_MATRIX_HEIGHT)):
- for x in range(math.ceil(LED_MATRIX_WIDTH)):
- r = fb[x][y] >> 16 & 0xFF
- g = fb[x][y] >> 8 & 0xFF
- b = fb[x][y] & 0xFF
- self.strip.setPixelColor(self.xy2i(x,
- math.ceil(LED_MATRIX_HEIGHT)-1-y),
- Color(r, g, b))
- self.strip.show()
- self.clock.tick(5)
-
-MODE_ALL_ON = 0
-MODE_WORM = 1
-MODE_CELLULAR_AUTOMATON = 2
-MODE_BLUE_GREEN_FLAMES = 3
-MODE_TEST_PATTERN = 4
-
-mode = MODE_BLUE_GREEN_FLAMES
-# mode = MODE_WORM
-# mode = MODE_CELLULAR_AUTOMATON
-
-def run(cmd_queue):
- global mode
- # Create NeoPixel object with appropriate configuration.
- strip = PixelStrip(math.ceil(LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT), LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
- # Intialize the library (must be called once before other functions).
- strip.begin()
- # This will be immediately replaced by the actual initial mode.
- p = Pattern(strip)
- current_mode = -1
- try:
- while True:
- if not cmd_queue.empty():
- mode = cmd_queue.get_nowait()
- print("Got {}".format(mode))
- if current_mode != mode:
- # External mode switch
- p.stop()
- current_mode = mode
- p = {
- MODE_ALL_ON : AllOn,
- MODE_WORM : Worm,
- MODE_CELLULAR_AUTOMATON : CellularAutomaton,
- MODE_BLUE_GREEN_FLAMES : BlueGreenFlames,
- MODE_TEST_PATTERN : TestPattern
- }[current_mode](strip)
- p.step()
- except KeyboardInterrupt:
- p.stop()
-
-def cmd_demo(cmd_queue):
- mode = 0
- while True:
- mode = (mode + 1) % 5
- print("Putting {}".format(mode))
- cmd_queue.put(mode)
- time.sleep(10)
-
-# Main program logic follows:
-if __name__ == '__main__':
- cmd_queue = multiprocessing.Queue()
- run = multiprocessing.Process(target=run, args=(cmd_queue,))
- run.start()
- if False:
- cmd_demo = multiprocessing.Process(target=cmd_demo, args=(cmd_queue,))
- cmd_demo.start()
- run.join()
diff --git a/templates/index.html b/templates/index.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html lang="de">
+ <head>
+ <meta charset="utf-8">
+ <title>LED-Steuerung</title>
+ <style>
+button{width:10em;}
+@media only screen and (max-device-width: 1024px){
+html{font-size : 300%; line-height: 50%;}
+button{font-size : 190%; width:10em;}
+.slider{
+ height: 5ex;
+ width: 40em;
+ autoScaleSlider:false,
+ autoHeight: false
+}
+ </style>
+ </head>
+ <body>
+ <h1>LED-Steuerung</h1>
+ <form action="/" method="POST">
+ <div class="slidecontainer">
+ R <input type="range" min="0" max="255" value="0" class="slider" id="slider_red">
+ <label id="label_red"></label><br />
+ G <input type="range" min="0" max="255" value="0" class="slider" id="slider_green">
+ <label id="label_green"></label><br />
+ B <input type="range" min="0" max="255" value="0" class="slider" id="slider_blue">
+ <label id="label_blue"></label><br />
+ </div><br />
+ <button type="submit" name="mode" value="worm">Wurm</button><br /><br />
+ <button type="submit" name="mode" value="cellular">Zellulärer Automat</button><br /><br />
+ <button type="submit" name="mode" value="flames">Feuer</button><br /><br />
+ <button type="submit" name="mode" value="test">Testmuster</button><br /><br />
+ </form>
+ <p><a href="config.html"</a>Konfigurieren</a></p>
+ <!-- Update slider on the fly -->
+ <script>
+ var slider_red = document.getElementById("slider_red");
+ var label_red = document.getElementById("label_red");
+ var slider_green = document.getElementById("slider_green");
+ var label_green = document.getElementById("label_green");
+ var slider_blue = document.getElementById("slider_blue");
+ var label_blue = document.getElementById("label_blue");
+ label_red.innerHTML = slider_red.value;
+ label_green.innerHTML = slider_green.value;
+ label_blue.innerHTML = slider_blue.value;
+ slider_red.oninput = function() {
+ label_red.innerHTML = slider_red.value;
+ label_green.innerHTML = slider_green.value;
+ label_blue.innerHTML = slider_blue.value;
+ var i = document.createElement("img");
+ // Didn't figure out how to do this with a POST request without
+ // resorting to external libraries
+ i.src = "?mode=rgb&red=" +
+ slider_red.value + "&green=" + slider_green.value +
+ "&blue=" + slider_blue.value;
+ };
+ slider_green.oninput = slider_red.oninput;
+ slider_blue.oninput = slider_red.oninput;
+ </script>
+ </body>
+</html>