b10a7d0396
This commit adds support for the Raspberry Pi, which allows users to create a completely standalone music visualization system. The Raspberry Pi should be connected directly to a ws2812b LED strip. A PWM-capable GPIO pin should be connected to the data line of the LED strip. A USB audio input device should be connected to one of the Raspberry Pi's USB ports. It is recommended that the GUI and FPS output be disabled when running the visualization on the Raspberry Pi. These features can degrade performance on the already computationally limited Raspberry Pi.
116 lines
3.8 KiB
Python
116 lines
3.8 KiB
Python
from __future__ import print_function
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
import numpy as np
|
|
import config
|
|
|
|
# ESP8266 uses WiFi communication
|
|
if config.DEVICE == 'esp8266':
|
|
import socket
|
|
_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
# Raspberry Pi controls the LED strip directly
|
|
elif config.DEVICE == 'pi':
|
|
import neopixel
|
|
strip = neopixel.Adafruit_NeoPixel(config.N_PIXELS, config.LED_PIN,
|
|
config.LED_FREQ_HZ, config.LED_DMA,
|
|
config.LED_INVERT, config.BRIGHTNESS)
|
|
strip.begin()
|
|
|
|
_gamma = np.load(config.GAMMA_TABLE_PATH)
|
|
"""Gamma lookup table used for nonlinear brightness correction"""
|
|
|
|
_prev_pixels = np.tile(253, (3, config.N_PIXELS))
|
|
"""Pixel values that were most recently displayed on the LED strip"""
|
|
|
|
pixels = np.tile(1, (3, config.N_PIXELS))
|
|
"""Pixel values for the LED strip"""
|
|
|
|
|
|
def _update_esp8266():
|
|
"""Sends UDP packets to ESP8266 to update LED strip values
|
|
|
|
The ESP8266 will receive and decode the packets to determine what values
|
|
to display on the LED strip. The communication protocol supports LED strips
|
|
with a maximum of 256 LEDs.
|
|
|
|
The packet encoding scheme is:
|
|
|i|r|g|b|
|
|
where
|
|
i (0 to 255): Index of LED to change (zero-based)
|
|
r (0 to 255): Red value of LED
|
|
g (0 to 255): Green value of LED
|
|
b (0 to 255): Blue value of LED
|
|
"""
|
|
global pixels, _prev_pixels
|
|
# Truncate values and cast to integer
|
|
pixels = np.clip(pixels, 0, 255).astype(long)
|
|
# Optionally apply gamma correctio
|
|
p = _gamma[pixels] if config.GAMMA_CORRECTION else np.copy(pixels)
|
|
# Send UDP packets when using ESP8266
|
|
m = []
|
|
for i in range(config.N_PIXELS):
|
|
# Ignore pixels if they haven't changed (saves bandwidth)
|
|
if np.array_equal(p[:, i], _prev_pixels[:, i]):
|
|
continue
|
|
# Byte
|
|
m.append(i) # Index of pixel to change
|
|
m.append(p[0][i]) # Pixel red value
|
|
m.append(p[1][i]) # Pixel green value
|
|
m.append(p[2][i]) # Pixel blue value
|
|
_prev_pixels = np.copy(p)
|
|
_sock.sendto(bytes(m), (config.UDP_IP, config.UDP_PORT))
|
|
|
|
|
|
def _update_pi():
|
|
"""Writes new LED values to the Raspberry Pi's LED strip
|
|
|
|
Raspberry Pi uses the rpi_ws281x to control the LED strip directly.
|
|
This function updates the LED strip with new values.
|
|
"""
|
|
global pixels, _prev_pixels
|
|
# Truncate values and cast to integer
|
|
pixels = np.clip(pixels, 0, 255).astype(long)
|
|
# Optional gamma correction
|
|
p = _gamma[pixels] if config.GAMMA_CORRECTION else np.copy(pixels)
|
|
# Encode 24-bit LED values in 32 bit integers
|
|
r = np.left_shift(p[0][:].astype(int), 8)
|
|
g = np.left_shift(p[1][:].astype(int), 16)
|
|
b = p[2][:].astype(int)
|
|
rgb = np.bitwise_or(np.bitwise_or(r, g), b)
|
|
# Update the pixels
|
|
for i in range(config.N_PIXELS):
|
|
# Ignore pixels if they haven't changed (saves bandwidth)
|
|
if np.array_equal(p[:, i], _prev_pixels[:, i]):
|
|
continue
|
|
strip._led_data[i] = rgb[i]
|
|
_prev_pixels = np.copy(p)
|
|
strip.show()
|
|
|
|
|
|
def update():
|
|
"""Updates the LED strip values"""
|
|
if config.DEVICE == 'esp8266':
|
|
_update_esp8266()
|
|
elif config.DEVICE == 'pi':
|
|
_update_pi()
|
|
else:
|
|
raise ValueError('Invalid device selected')
|
|
|
|
|
|
# Execute this file to run a LED strand test
|
|
# If everything is working, you should see a red, green, and blue pixel scroll
|
|
# across the LED strip continously
|
|
if __name__ == '__main__':
|
|
import time
|
|
# Turn all pixels off
|
|
pixels *= 0
|
|
pixels[0, 0] = 255 # Set 1st pixel red
|
|
pixels[1, 1] = 255 # Set 2nd pixel green
|
|
pixels[2, 2] = 255 # Set 3rd pixel blue
|
|
print('Starting LED strand test')
|
|
while True:
|
|
pixels = np.roll(pixels, 1, axis=1)
|
|
update()
|
|
time.sleep(0.01)
|