Init
This commit is contained in:
commit
cba3dd96d6
|
@ -0,0 +1,20 @@
|
|||
DESTDIR :=
|
||||
ROOTDIR=/usr/share/pumpkin/
|
||||
SERVICE_PATH=/etc/systemd/system/
|
||||
|
||||
install:
|
||||
pip install RPi.GPIO
|
||||
mkdir -p $(DESTDIR)$(ROOTDIR)
|
||||
mkdir -p $(DESTDIR)$(SERVICE_PATH)
|
||||
cp __main__.py $(DESTDIR)$(ROOTDIR)
|
||||
cp rgb.py $(DESTDIR)$(ROOTDIR)
|
||||
cp waveloader.py $(DESTDIR)$(ROOTDIR)
|
||||
cp config.py $(DESTDIR)$(ROOTDIR)
|
||||
cp pumpkin.py $(DESTDIR)$(ROOTDIR)
|
||||
cp pumpkin.service $(DESTDIR)$(SERVICE_PATH)
|
||||
systemctl daemon-reload
|
||||
|
||||
uninstall:
|
||||
rm $(SERVICE_PATH)pumpkin.service
|
||||
rm -R $(ROOTDIR)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
RUN THE INSTALLER BY TYPING IN:
|
||||
./installer
|
||||
|
||||
IF SOME DEPENDENCIES ARE MISSING PLEASE INSTALL THEM WITH YOUR PACKAGE MANAGER.
|
||||
|
||||
IF THE INSTALLER WAS SUCCESSFULL YOU HAVE TO START IT ONCE TO GET A DEFAULT CONFIG FILE:
|
||||
sudo systemctl start pumpkin
|
||||
|
||||
IT WILL CRASH BUT NOW YOU WILL HAVE A CUSTOMIZABLE CONFIG FILE. OPEN IT:
|
||||
sudo nano /etc/pumpkin.conf
|
||||
|
||||
TO MAKE IT START SUCCESSFULLY YOU HAVE TO GIVE THE ABSOLUTE PATH TO YOUR WAVE FOLDER:
|
||||
AUDIO_PATH=/your/wave/folder
|
||||
|
||||
THEN SPECIFY YOUR GPIOS FOR EVERY COLOR. IF THERE ARE MULTIPLE LEDS FOR ONE COLOR SEPARATE THEM WITH , LIKE THAT:
|
||||
GPIOS_RED=11,12,13
|
||||
|
||||
NOW YOU HAVE A BASIC CONFIGURATION AND IT SHOULD RUN. START THE PUMPKIN AGAIN BY TYPING IN
|
||||
sudo systemctl start pumpkin
|
||||
|
||||
TO STOP IT TYPE
|
||||
sudo systemctl stop pumpkin
|
||||
|
||||
IF YOU WANT TO MAKE IT START AUTOMATICALLY AFTER BOOT WITHOUT LOGIN TYPE
|
||||
sudo systemctl enable pumpkin
|
||||
|
||||
TO DELETE THE AUTOSTART TYPE
|
||||
sudo systemctl disable pumpkin
|
||||
|
||||
IF YOU HAVE A FOG MACHINE YOU CAN ALSO SPECIFY ONE GPIO FOR THE EVAPORATOR AND ONE FOR THE PUMP:
|
||||
GPIO_EVAPORATOR=
|
||||
GPIO_PUMP=
|
||||
|
||||
AND NOW HAVE FUN!
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from pumpkin import Pumpkin
|
||||
from config import Config
|
||||
from time import time
|
||||
from time import sleep
|
||||
from os import listdir
|
||||
from os.path import isfile
|
||||
from shutil import copy
|
||||
from sys import argv
|
||||
import atexit
|
||||
|
||||
|
||||
def cleanup():
|
||||
pumpkin.cleanup()
|
||||
|
||||
|
||||
CONFIG_PATH = '/etc/pumpkin.conf'
|
||||
|
||||
# Make sure that a config file exists
|
||||
if not isfile(CONFIG_PATH):
|
||||
parts = argv[0].split('/')
|
||||
pathDefaultConf = '/'.join(parts[:len(parts) - 1]) + '/pumpkin.conf'
|
||||
|
||||
print(pathDefaultConf)
|
||||
copy(pathDefaultConf, CONFIG_PATH)
|
||||
|
||||
config = Config(CONFIG_PATH)
|
||||
|
||||
AUDIO_PATH = config.getString('AUDIO_PATH')
|
||||
|
||||
COLOR_RED = config.getInt('COLOR_RED')
|
||||
COLOR_GREEN = config.getInt('COLOR_GREEN')
|
||||
COLOR_BLUE = config.getInt('COLOR_BLUE')
|
||||
|
||||
COLOR_BACKGROUND_RED = config.getInt('COLOR_BACKGROUND_RED')
|
||||
COLOR_BACKGROUND_GREEN = config.getInt('COLOR_BACKGROUND_GREEN')
|
||||
COLOR_BACKGROUND_BLUE = config.getInt('COLOR_BACKGROUND_BLUE')
|
||||
|
||||
COLOR_FIRE_RED = config.getInt('COLOR_FIRE_RED')
|
||||
COLOR_FIRE_GREEN = config.getInt('COLOR_FIRE_GREEN')
|
||||
COLOR_FIRE_BLUE = config.getInt('COLOR_FIRE_BLUE')
|
||||
|
||||
GPIOS_RED = config.getIntList('GPIOS_RED')
|
||||
GPIOS_GREEN = config.getIntList('GPIOS_GREEN')
|
||||
GPIOS_BLUE = config.getIntList('GPIOS_BLUE')
|
||||
GPIO_TRIGGER = config.getInt('GPIO_TRIGGER')
|
||||
|
||||
GPIO_EVAPORATOR = config.getInt('GPIO_EVAPORATOR')
|
||||
GPIO_PUMP = config.getInt('GPIO_PUMP')
|
||||
FOG_RUNTIME_MAX = config.getFloat('FOG_RUNTIME_MAX')
|
||||
|
||||
GPIO_POWEROFF = config.getInt('GPIO_POWEROFF')
|
||||
RETRIGGER_TIMEOUT = config.getFloat('RETRIGGER_TIMEOUT')
|
||||
|
||||
PWM_FREQUENCY = config.getInt('PWM_FREQUENCY')
|
||||
|
||||
pumpkin = Pumpkin(AUDIO_PATH, GPIOS_RED, GPIOS_GREEN, GPIOS_BLUE, GPIO_TRIGGER, PWM_FREQUENCY)
|
||||
pumpkin.setAudioColor(COLOR_RED, COLOR_GREEN, COLOR_BLUE)
|
||||
pumpkin.setBackgroundColor(COLOR_BACKGROUND_RED, COLOR_BACKGROUND_GREEN, COLOR_BACKGROUND_BLUE)
|
||||
pumpkin.setFireColor(COLOR_FIRE_RED, COLOR_FIRE_GREEN, COLOR_FIRE_BLUE)
|
||||
pumpkin.enableFog(GPIO_EVAPORATOR, GPIO_PUMP, FOG_RUNTIME_MAX)
|
||||
pumpkin.enablePowerOff(GPIO_POWEROFF)
|
||||
|
||||
atexit.register(cleanup)
|
||||
|
||||
retriggerTimeout = RETRIGGER_TIMEOUT
|
||||
t_after_trigger = time() - retriggerTimeout
|
||||
|
||||
while True:
|
||||
|
||||
try:
|
||||
if pumpkin.isTriggered() and time() - t_after_trigger > retriggerTimeout:
|
||||
pumpkin.speak()
|
||||
t_after_trigger = time()
|
||||
else:
|
||||
pumpkin.lurk()
|
||||
|
||||
sleep(0.5)
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
exit()
|
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
class Config:
|
||||
def __init__(self, file):
|
||||
self.COMMENT_MARK = '#'
|
||||
|
||||
self.path = file
|
||||
|
||||
try:
|
||||
with open(file, 'r') as conf:
|
||||
config = conf.read().split('\n')
|
||||
except:
|
||||
raise IOError('Couldn\'t read ' + file + '!')
|
||||
|
||||
if not self.isValidFile(config):
|
||||
raise Exception('Error: ' + file + ' is no valid config file!')
|
||||
|
||||
self.values = {}
|
||||
|
||||
for c in config:
|
||||
if self.lineHasValue(c):
|
||||
parts = c.split('=')
|
||||
self.values[parts[0].strip()] = parts[1].strip()
|
||||
|
||||
def getValue(self, key):
|
||||
try:
|
||||
return self.values[key]
|
||||
except:
|
||||
return None
|
||||
|
||||
def getInt(self, key):
|
||||
value = self.getValue(key)
|
||||
|
||||
if value == None:
|
||||
return None
|
||||
|
||||
return int(value)
|
||||
|
||||
def getFloat(self, key):
|
||||
return float(self.getValue(key))
|
||||
|
||||
def getBool(self, key):
|
||||
return bool(self.getValue(key))
|
||||
|
||||
def getString(self, key):
|
||||
return self.getValue(key)
|
||||
|
||||
def getList(self, key):
|
||||
values = []
|
||||
|
||||
for v in self.getValue(key).split(','):
|
||||
values.append(v.strip())
|
||||
|
||||
return values
|
||||
|
||||
def getIntList(self, key):
|
||||
values = []
|
||||
|
||||
for i in self.getList(key):
|
||||
values.append(int(i))
|
||||
|
||||
return values
|
||||
|
||||
def lineHasValue(self, line):
|
||||
return not line.startswith(self.COMMENT_MARK) and len(line.replace(' ', '').replace('\n', '')) > 0 and line.count('=') == 1
|
||||
|
||||
def lineHasSyntaxError(self, line):
|
||||
return not line.startswith(self.COMMENT_MARK) and len(line.replace(' ', '').replace('\n', '')) > 0 and line.count('=') != 1
|
||||
|
||||
def isValidFile(self, config):
|
||||
count = 1
|
||||
for line in config:
|
||||
if self.lineHasSyntaxError(line):
|
||||
print(self.path + ' Line ' + str(count) + ': Invalid syntax!')
|
||||
count += 1
|
||||
|
||||
return True
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/bash
|
||||
|
||||
function check_package()
|
||||
{
|
||||
printf "\tChecking for ${1}... "
|
||||
|
||||
${1} --version &> /dev/null
|
||||
|
||||
if [ ! $? == 0 ]
|
||||
then
|
||||
echo "Failed!"
|
||||
echo ""
|
||||
echo "Please install ${1}!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found."
|
||||
}
|
||||
|
||||
echo "Checking for dependencies:"
|
||||
|
||||
check_package python3
|
||||
check_package pip3
|
||||
check_package gcc
|
||||
check_package make
|
||||
check_package aplay
|
||||
|
||||
echo ""
|
||||
echo "Ready to install."
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
./configure
|
||||
|
||||
if [ $? == 0 ]
|
||||
then
|
||||
echo "Installing..."
|
||||
sudo make install
|
||||
fi
|
|
@ -0,0 +1,58 @@
|
|||
# The GPIO numbers are in board mode so you can count down on your
|
||||
# Raspberry Pi to find the number:
|
||||
# _____________
|
||||
# (1) (2) \
|
||||
# (3) (4) |
|
||||
# (5) (6) |
|
||||
#
|
||||
# and so on...
|
||||
#
|
||||
# To get a view for the numbering install the python module gpiozero the following way:
|
||||
#
|
||||
# sudo pip install gpiozero
|
||||
#
|
||||
# Now if you type pinout you get a nice graphical overview for your pins.
|
||||
# WARNING: Only use GPIOs that are NOT reserved for I²C, SPI or UART!
|
||||
|
||||
PWM_FREQUENCY=60
|
||||
|
||||
# You can specify multiple GPIOS for one color separated by ,
|
||||
GPIOS_RED=11
|
||||
GPIOS_GREEN=12
|
||||
GPIOS_BLUE=13
|
||||
|
||||
# The path to your audio folder (must contain wave files!).
|
||||
AUDIO_PATH=/path/to/audio/files
|
||||
|
||||
# The color for sound visualization
|
||||
COLOR_RED=0
|
||||
COLOR_GREEN=128
|
||||
COLOR_BLUE=255
|
||||
|
||||
# The color for silence parts of your audio visualization.
|
||||
COLOR_BACKGROUND_RED=0
|
||||
COLOR_BACKGROUND_GREEN=0
|
||||
COLOR_BACKGROUND_BLUE=0
|
||||
|
||||
# The color for the flickering candle on standby.
|
||||
COLOR_FIRE_RED=255
|
||||
COLOR_FIRE_GREEN=128
|
||||
COLOR_FIRE_BLUE=0
|
||||
|
||||
# The input pin for your motion sensor.
|
||||
GPIO_TRIGGER=7
|
||||
|
||||
# The seconds the pumpkin waits to be triggered again after he has been triggered.
|
||||
RETRIGGER_TIMEOUT=10
|
||||
|
||||
# The input pin for your evaporator activation.
|
||||
GPIO_EVAPORATOR=0
|
||||
|
||||
# The input pin for your pump activation.
|
||||
GPIO_PUMP=0
|
||||
|
||||
# The maximal runtime for pump and evaporator in seconds during speaking.
|
||||
FOG_RUNTIME_MAX=3
|
||||
|
||||
# The input pin for a poweroff switch to power down the whole system.
|
||||
GPIO_POWEROFF=0
|
|
@ -0,0 +1,184 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import _thread
|
||||
import atexit
|
||||
import RPi.GPIO as GPIO
|
||||
import os
|
||||
from waveloader import WaveLoader
|
||||
from rgb import RGBMatrix
|
||||
from rgb import RGB
|
||||
from subprocess import call
|
||||
from time import time
|
||||
from random import randint
|
||||
|
||||
|
||||
class Pumpkin:
|
||||
def __init__(self, soundpath, GPIOS_RED = [11], GPIOS_GREEN = [12], GPIOS_BLUE = [13], GPIO_TRIGGER = 15, PWM_FREQUENCY = 60):
|
||||
self.soundfiles = []
|
||||
self.loadWaveFolder(soundpath)
|
||||
|
||||
if len(self.soundfiles) == 0:
|
||||
raise IOError('No audio files found in ' + soundpath + '!')
|
||||
|
||||
GPIO.setmode(GPIO.BOARD)
|
||||
|
||||
self.GPIOS_RED = GPIOS_RED
|
||||
self.GPIOS_GREEN = GPIOS_GREEN
|
||||
self.GPIOS_BLUE = GPIOS_BLUE
|
||||
self.GPIO_TRIGGER = GPIO_TRIGGER
|
||||
self.GPIO_EVAPORATOR = 0
|
||||
self.GPIO_PUMP = 0
|
||||
self.GPIO_POWEROFF = 0
|
||||
|
||||
self.fogRuntimeMax = 3
|
||||
|
||||
GPIO.setup(self.GPIO_TRIGGER, GPIO.IN)
|
||||
|
||||
self.rgbControl = RGBMatrix(PWM_FREQUENCY)
|
||||
|
||||
for r in self.GPIOS_RED:
|
||||
self.rgbControl.addRed(r)
|
||||
|
||||
for g in self.GPIOS_GREEN:
|
||||
self.rgbControl.addGreen(g)
|
||||
|
||||
for b in self.GPIOS_BLUE:
|
||||
self.rgbControl.addBlue(b)
|
||||
|
||||
self.color = RGB(255, 255, 255)
|
||||
self.colorBackground = RGB(0, 0, 0)
|
||||
self.colorFire = RGB(255, 55, 0)
|
||||
|
||||
atexit.register(self.cleanup)
|
||||
|
||||
def __del__(self):
|
||||
self.cleanup()
|
||||
|
||||
def cleanup(self):
|
||||
GPIO.cleanup()
|
||||
|
||||
def enableFog(self, GPIO_EVAPORATOR, GPIO_PUMP, runtimeSecondsMax = 3):
|
||||
self.GPIO_EVAPORATOR = GPIO_EVAPORATOR
|
||||
self.GPIO_PUMP = GPIO_PUMP
|
||||
self.fogRuntimeMax = runtimeSecondsMax
|
||||
|
||||
if self.GPIO_EVAPORATOR != 0:
|
||||
GPIO.setup(self.GPIO_EVAPORATOR, GPIO.OUT)
|
||||
|
||||
if self.GPIO_PUMP != 0:
|
||||
GPIO.setup(self.GPIO_PUMP, GPIO.OUT)
|
||||
|
||||
self.stopFog()
|
||||
|
||||
def enablePowerOff(self, GPIO_POWEROFF):
|
||||
self.GPIO_POWEROFF = GPIO_POWEROFF
|
||||
|
||||
if self.GPIO_POWEROFF != 0:
|
||||
self.powerSwitch = GPIO.setup(self.GPIO_POWEROFF, GPIO.IN)
|
||||
|
||||
def loadWaveFolder(self, path):
|
||||
if not path.endswith('/'):
|
||||
path += '/'
|
||||
|
||||
files = os.listdir(path)
|
||||
|
||||
for f in files:
|
||||
if f.endswith('.wav'):
|
||||
self.soundfiles.append(path + f)
|
||||
|
||||
def lurk(self):
|
||||
if self.isTurnedOff():
|
||||
self.poweroff()
|
||||
|
||||
self.rgbControl.candle(self.colorFire.r, self.colorFire.g, self.colorFire.b, 0.5)
|
||||
|
||||
def speak(self):
|
||||
self.startFog()
|
||||
|
||||
soundfile = self.soundfiles[randint(0, len(self.soundfiles) - 1)]
|
||||
|
||||
sound = WaveLoader(soundfile)
|
||||
|
||||
duration = sound.frameCount / sound.frameRate
|
||||
t_now = 0
|
||||
|
||||
player = _thread.start_new_thread(call, (['aplay', soundfile],))
|
||||
t_start = time()
|
||||
|
||||
while t_now < duration:
|
||||
t_now = time() - t_start
|
||||
amplitude = sound.getCurrentLevelRealtime(t_now)
|
||||
|
||||
if amplitude < 0.1:
|
||||
red = self.colorBackground.r
|
||||
green = self.colorBackground.g
|
||||
blue = self.colorBackground.b
|
||||
else:
|
||||
amplitude = 1
|
||||
red = self.color.r * amplitude
|
||||
green = self.color.g * amplitude
|
||||
blue = self.color.b * amplitude
|
||||
|
||||
self.rgbControl.setColor(red, green, blue)
|
||||
|
||||
if t_now > self.fogRuntimeMax:
|
||||
self.stopFog()
|
||||
|
||||
self.stopFog()
|
||||
|
||||
def startFog(self):
|
||||
if self.GPIO_EVAPORATOR != 0:
|
||||
GPIO.output(self.GPIO_EVAPORATOR, True)
|
||||
|
||||
if self.GPIO_PUMP != 0:
|
||||
GPIO.output(self.GPIO_PUMP, True)
|
||||
|
||||
def stopFog(self):
|
||||
if self.GPIO_EVAPORATOR != 0:
|
||||
GPIO.output(self.GPIO_EVAPORATOR, False)
|
||||
|
||||
if self.GPIO_PUMP != 0:
|
||||
GPIO.output(self.GPIO_PUMP, False)
|
||||
|
||||
def isTriggered(self):
|
||||
return bool(GPIO.input(self.GPIO_TRIGGER))
|
||||
|
||||
def isTurnedOff(self):
|
||||
if self.GPIO_POWEROFF == 0:
|
||||
return False
|
||||
|
||||
return bool(GPIO.input(self.GPIO_POWEROFF))
|
||||
|
||||
def poweroff(self):
|
||||
call(['poweroff'])
|
||||
|
||||
def setAudioColor(self, red, green, blue):
|
||||
self.color = RGB(red, green, blue)
|
||||
|
||||
def setBackgroundColor(self, red, green, blue):
|
||||
self.colorBackground = RGB(red, green, blue)
|
||||
|
||||
def setFireColor(self, red, green, blue):
|
||||
self.colorFire = RGB(red, green, blue)
|
||||
|
||||
def loadAudioFolder(self, path):
|
||||
if os.path.isdir(path):
|
||||
if not path.endswith('/'):
|
||||
path += '/'
|
||||
|
||||
for file in os.listdir(path):
|
||||
if file.endswith('.wav'):
|
||||
audioFiles.append(audioPath + file)
|
||||
|
||||
@staticmethod
|
||||
def findAudioFiles():
|
||||
audioFiles = []
|
||||
|
||||
for home in os.listdir('/home'):
|
||||
audioPath = '/home/' + home + '/.config/pumpkin/audio/'
|
||||
|
||||
if os.path.isdir(audioPath):
|
||||
for file in os.listdir(audioPath):
|
||||
audioFiles.append(audioPath + file)
|
||||
|
||||
return audioFiles
|
|
@ -0,0 +1,5 @@
|
|||
[Service]
|
||||
ExecStart=/usr/share/pumpkin/__main__.py
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,387 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import RPi.GPIO as GPIO
|
||||
from time import sleep
|
||||
from time import time
|
||||
from random import randint
|
||||
|
||||
|
||||
class RGB:
|
||||
def __init__(self, red, green, blue):
|
||||
self.r = red
|
||||
self.g = green
|
||||
self.b = blue
|
||||
|
||||
def getDutyCycles(self):
|
||||
percent = RGB.rgbToPerc(self.r, self.g, self.b)
|
||||
|
||||
return RGB(percent[0], percent[1], percent[2])
|
||||
|
||||
@staticmethod
|
||||
def sanitizeColor(value):
|
||||
if value < 0 or value > 255:
|
||||
print('Value must be between 0 and 255! Fixing color value.')
|
||||
return max(0, min(value, 255))
|
||||
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def rgbToPerc(red, green, blue):
|
||||
percent = 100 / 255
|
||||
|
||||
return (red * percent, green * percent, blue * percent)
|
||||
|
||||
@staticmethod
|
||||
def percToRGB(red, green, blue):
|
||||
percent = 255 / 100
|
||||
|
||||
return (red * percent, green * percent, blue * percent)
|
||||
|
||||
|
||||
class RGB_Control:
|
||||
|
||||
def __init__(self, r, g, b, freq=60):
|
||||
self.pins_allowed = (7, 11, 12, 13, 15, 16, 18, 19, 21, 22)
|
||||
self.frequency = freq
|
||||
|
||||
if r not in self.pins_allowed:
|
||||
print("Fehler: r wurde ungültigem Pin zugewiesen!")
|
||||
exit()
|
||||
|
||||
if g not in self.pins_allowed:
|
||||
print("Fehler: g wurde ungültigem Pin zugewiesen!")
|
||||
exit()
|
||||
|
||||
if b not in self.pins_allowed:
|
||||
print("Fehler: b wurde ungültigem Pin zugewiesen!")
|
||||
exit()
|
||||
|
||||
try:
|
||||
GPIO.setup(r, GPIO.OUT)
|
||||
GPIO.setup(g, GPIO.OUT)
|
||||
GPIO.setup(b, GPIO.OUT)
|
||||
except:
|
||||
GPIO.setmode(GPIO.BOARD)
|
||||
|
||||
GPIO.setup(r, GPIO.OUT)
|
||||
GPIO.setup(g, GPIO.OUT)
|
||||
GPIO.setup(b, GPIO.OUT)
|
||||
|
||||
self.r = GPIO.PWM(r, freq)
|
||||
self.g = GPIO.PWM(g, freq)
|
||||
self.b = GPIO.PWM(b, freq)
|
||||
|
||||
self.r.start(0)
|
||||
self.g.start(0)
|
||||
self.b.start(0)
|
||||
|
||||
self.r_alt = None
|
||||
self.g_alt = None
|
||||
self.b_alt = None
|
||||
|
||||
self.intensity = 1
|
||||
|
||||
self.current_r = 0
|
||||
self.current_g = 0
|
||||
self.current_b = 0
|
||||
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
GPIO.cleanup()
|
||||
except:
|
||||
pass
|
||||
|
||||
def enableSecondLED(self, gpioRed, gpioGreen, gpioBlue):
|
||||
GPIO.setup(gpioRed, GPIO.OUT)
|
||||
GPIO.setup(gpioGreen, GPIO.OUT)
|
||||
GPIO.setup(gpioBlue, GPIO.OUT)
|
||||
|
||||
self.r_alt = GPIO.PWM(gpioRed, self.frequency)
|
||||
self.g_alt = GPIO.PWM(gpioGreen, self.frequency)
|
||||
self.b_alt = GPIO.PWM(gpioBlue, self.frequency)
|
||||
|
||||
self.r_alt.start(0)
|
||||
self.g_alt.start(0)
|
||||
self.b_alt.start(0)
|
||||
|
||||
def toggleSpectrum(self,fps=30,speed=1):
|
||||
self.setColor(255,0,0)
|
||||
|
||||
self.transition((255, 255, 0), duration = speed)
|
||||
self.transition((0, 255, 0), duration = speed)
|
||||
self.transition((0, 255, 255), duration = speed)
|
||||
self.transition((0, 0, 255), duration = speed)
|
||||
self.transition((255, 0, 255), duration = speed)
|
||||
self.transition((255, 0, 0), duration = speed)
|
||||
|
||||
|
||||
def flash(self, color = (255, 255, 255)):
|
||||
color_start = (self.current_r, self.current_g, self.current_b)
|
||||
self.setColor(color[0], color[1], color[2])
|
||||
sleep(1/30)
|
||||
self.setColor(color_start[0], color_start[1], color_start[2])
|
||||
|
||||
|
||||
def pulse(self, from_color, to_color, freq=0.5):
|
||||
duration = freq * 0.5
|
||||
|
||||
self.transition(from_color, duration=duration)
|
||||
self.transition(to_color, duration=duration)
|
||||
|
||||
|
||||
def fire(self, wind = 0.5, r = 255, g = 55, b = 0):
|
||||
wind_power = 100 - (100 * wind)
|
||||
|
||||
flicker = randint(int(wind_power), 100) * 0.01
|
||||
|
||||
color_start = (r * flicker, g * flicker, b * flicker)
|
||||
|
||||
duration = randint(10, int(40 - wind * 30)) * 0.01
|
||||
|
||||
self.transition(color_start, duration = duration)
|
||||
|
||||
def panic(self, times = 10, color = (255, 255, 255)):
|
||||
for t in range(times):
|
||||
self.flash(color = color)
|
||||
sleep(1/30)
|
||||
|
||||
def setColor(self, r, g, b):
|
||||
perc = RGB.rgbToPerc(r, g, b)
|
||||
|
||||
self.current_r = r
|
||||
self.current_g = g
|
||||
self.current_b = b
|
||||
|
||||
self.r.ChangeDutyCycle(perc[0] * self.intensity)
|
||||
self.g.ChangeDutyCycle(perc[1] * self.intensity)
|
||||
self.b.ChangeDutyCycle(perc[2] * self.intensity)
|
||||
|
||||
if self.r_alt != None:
|
||||
self.r_alt.ChangeDutyCycle(perc[0] * self.intensity)
|
||||
|
||||
if self.g_alt != None:
|
||||
self.g_alt.ChangeDutyCycle(perc[1] * self.intensity)
|
||||
|
||||
if self.b_alt != None:
|
||||
self.b_alt.ChangeDutyCycle(perc[2] * self.intensity)
|
||||
|
||||
def transition(self, color_finish, duration = 3, fps = 30):
|
||||
start_r = self.current_r
|
||||
start_g = self.current_g
|
||||
start_b = self.current_b
|
||||
|
||||
finish_r = color_finish[0]
|
||||
finish_g = color_finish[1]
|
||||
finish_b = color_finish[2]
|
||||
|
||||
delta_r = finish_r - start_r
|
||||
delta_g = finish_g - start_g
|
||||
delta_b = finish_b - start_b
|
||||
|
||||
|
||||
frames = duration * fps
|
||||
frame_duration = 1 / fps
|
||||
|
||||
# Step per frame for every color
|
||||
stf_r = delta_r / frames
|
||||
stf_g = delta_g / frames
|
||||
stf_b = delta_b / frames
|
||||
|
||||
self.setColor(self.current_r, self.current_g, self.current_b)
|
||||
sleep(frame_duration)
|
||||
|
||||
start_time = time()
|
||||
|
||||
while time() - start_time < duration:
|
||||
|
||||
self.current_r += stf_r
|
||||
self.current_g += stf_g
|
||||
self.current_b += stf_b
|
||||
|
||||
if self.current_r > 255:
|
||||
self.current_r = 255
|
||||
elif self.current_r < 0:
|
||||
self.current_r = 0
|
||||
|
||||
if self.current_g > 255:
|
||||
self.current_g = 255
|
||||
elif self.current_g < 0:
|
||||
self.current_g = 0
|
||||
|
||||
if self.current_b > 255:
|
||||
self.current_b = 255
|
||||
elif self.current_b < 0:
|
||||
self.current_b = 0
|
||||
|
||||
self.setColor(self.current_r, self.current_g, self.current_b)
|
||||
|
||||
sleep(frame_duration)
|
||||
|
||||
|
||||
class RGBMatrix(RGB):
|
||||
|
||||
def __init__(self, PWM_FREQUENCY = 60):
|
||||
self.PWM_FREQUENCY = PWM_FREQUENCY
|
||||
|
||||
self.gpiosRed = []
|
||||
self.gpiosGreen = []
|
||||
self.gpiosBlue = []
|
||||
|
||||
self.currentColor = RGB(0, 0, 0)
|
||||
|
||||
GPIO.setmode(GPIO.BOARD)
|
||||
|
||||
def __del__(self):
|
||||
GPIO.cleanup()
|
||||
|
||||
def setupLED(self, gpio):
|
||||
GPIO.setup(gpio, GPIO.OUT)
|
||||
led = GPIO.PWM(gpio, self.PWM_FREQUENCY)
|
||||
led.start(0)
|
||||
|
||||
return led
|
||||
|
||||
def addRed(self, gpio):
|
||||
led = self.setupLED(gpio)
|
||||
led.ChangeDutyCycle(self.currentColor.getDutyCycles().r)
|
||||
self.gpiosRed.append(led)
|
||||
|
||||
def addGreen(self, gpio):
|
||||
led = self.setupLED(gpio)
|
||||
led.ChangeDutyCycle(self.currentColor.getDutyCycles().g)
|
||||
self.gpiosGreen.append(led)
|
||||
|
||||
def addBlue(self, gpio):
|
||||
led = self.setupLED(gpio)
|
||||
led.ChangeDutyCycle(self.currentColor.getDutyCycles().b)
|
||||
self.gpiosBlue.append(led)
|
||||
|
||||
def setColor(self, red, green, blue):
|
||||
rgb = RGB(red, green, blue)
|
||||
|
||||
colorsDutyCycle = rgb.getDutyCycles()
|
||||
|
||||
for red in self.gpiosRed:
|
||||
red.ChangeDutyCycle(colorsDutyCycle.r)
|
||||
|
||||
for green in self.gpiosGreen:
|
||||
green.ChangeDutyCycle(colorsDutyCycle.g)
|
||||
|
||||
for blue in self.gpiosBlue:
|
||||
blue.ChangeDutyCycle(colorsDutyCycle.b)
|
||||
|
||||
self.currentColor = RGB(rgb.r, rgb.g, rgb.b)
|
||||
|
||||
def fadeToColor(self, red, green, blue, seconds, fps = 30):
|
||||
timeStart = time()
|
||||
timeEnd = timeStart + seconds
|
||||
frameDuration = 1 / fps
|
||||
|
||||
colorStart = self.currentColor
|
||||
colorTarget = RGB(red, green, blue)
|
||||
|
||||
deltaRed = colorTarget.r - colorStart.r
|
||||
deltaGreen = colorTarget.g - colorStart.g
|
||||
deltaBlue = colorTarget.b - colorStart.b
|
||||
|
||||
while time() < timeEnd:
|
||||
frameStart = time()
|
||||
|
||||
progress = (time() - timeStart) / seconds
|
||||
|
||||
self.setColor(
|
||||
colorStart.r + deltaRed * progress,
|
||||
colorStart.g + deltaGreen * progress,
|
||||
colorStart.b + deltaBlue * progress
|
||||
)
|
||||
|
||||
duration = time() - frameStart
|
||||
|
||||
if duration < frameDuration:
|
||||
sleep(frameDuration - duration)
|
||||
|
||||
self.setColor(red, green, blue)
|
||||
|
||||
def candle(self, red, green, blue, wind = 0.5):
|
||||
darkenMax = 100 - wind * 100
|
||||
color = RGB(red, green, blue)
|
||||
|
||||
intensity = randint(darkenMax, 100) / 100
|
||||
seconds = randint(darkenMax, 50) / 100
|
||||
|
||||
targetColor = RGB(color.r * intensity, color.g * intensity, color.b * intensity)
|
||||
self.fadeToColor(targetColor.r, targetColor.g, targetColor.b, seconds)
|
||||
|
||||
# Chance of 5% for flickering
|
||||
flickering = randint(0, 100) < 5
|
||||
|
||||
if flickering:
|
||||
intensityLight = 1
|
||||
intensityDark = 0.9
|
||||
times = randint(4, 10)
|
||||
duration = randint(5, 30) / 100
|
||||
|
||||
colorLight = RGB(color.r * intensityLight, color.g * intensityLight, color.b * intensityLight)
|
||||
colorDark = RGB(color.r * intensityDark, color.g * intensityDark, color.b * intensityDark)
|
||||
|
||||
intensity = 1
|
||||
|
||||
for t in range(times):
|
||||
progress = (1 / times) * t
|
||||
intensity = abs(0.5 - progress) * 2
|
||||
colorDark = RGB(
|
||||
color.r * intensityDark * intensity,
|
||||
color.g * intensityDark * intensity,
|
||||
color.b * intensityDark * intensity
|
||||
)
|
||||
self.fadeToColor(colorLight.r, colorLight.g, colorLight.b, duration)
|
||||
self.fadeToColor(colorDark.r, colorDark.g, colorDark.b, duration)
|
||||
|
||||
def toggleSpectrum(self, seconds):
|
||||
spectrum = [
|
||||
RGB(255, 0, 0),
|
||||
RGB(255, 255, 0),
|
||||
RGB(0, 255, 0),
|
||||
RGB(0, 255, 255),
|
||||
RGB(0, 0, 255),
|
||||
RGB(255, 0, 255)
|
||||
]
|
||||
|
||||
fadeDuration = seconds / len(spectrum)
|
||||
|
||||
for color in spectrum:
|
||||
self.fadeToColor(color.r, color.g, color.b, fadeDuration)
|
||||
|
||||
def halloween(control):
|
||||
|
||||
while True:
|
||||
flash = randint(1, 100)
|
||||
if flash >= 1 and flash <= 5:
|
||||
t = randint(1, 3)
|
||||
control.panic(times = t, color = (200, 255, 255))
|
||||
|
||||
control.fire(wind = 0.8)
|
||||
|
||||
|
||||
def spectrum(control):
|
||||
while True:
|
||||
control.toggleSpectrum()
|
||||
|
||||
|
||||
def fire(control, wind = 0.5):
|
||||
while True:
|
||||
control.fire(wind = wind)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
|
||||
control = RGB_Control(11, 12, 13)
|
||||
|
||||
fire(control, wind = 0.9)
|
||||
|
||||
GPIO.cleanup()
|
||||
|
||||
except:
|
||||
del control
|
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import wave
|
||||
|
||||
|
||||
class WaveLoader:
|
||||
def __init__(self, filename):
|
||||
self.soundfile = wave.open(filename, 'rb')
|
||||
|
||||
self.channels = self.soundfile.getnchannels()
|
||||
self.sampleWidth = self.soundfile.getsampwidth()
|
||||
self.frameCount = self.soundfile.getnframes()
|
||||
self.frameRate = self.soundfile.getframerate()
|
||||
self.filename = filename
|
||||
self.amplitudeMax = 2 ** (self.sampleWidth * 8)
|
||||
self.levelData = {}
|
||||
self.amplitudeData = {}
|
||||
|
||||
def __del__(self):
|
||||
self.soundfile.close()
|
||||
|
||||
def getCurrentLevelRealtime(self, seconds):
|
||||
frameIndex = int(round(seconds * self.frameRate))
|
||||
|
||||
if frameIndex >= self.frameCount:
|
||||
return 0
|
||||
|
||||
self.soundfile.setpos(frameIndex)
|
||||
|
||||
level = int.from_bytes(self.soundfile.readframes(1)[0:self.sampleWidth], 'little')
|
||||
|
||||
if level > self.amplitudeMax * 0.5:
|
||||
level -= self.amplitudeMax
|
||||
|
||||
return abs(level) / (self.amplitudeMax * 0.5)
|
||||
|
||||
def getLevel(self, channel = 0):
|
||||
if channel < self.channels:
|
||||
return self.levelData[channel].copy()
|
||||
|
||||
def getAmplitude(self, channel = 0):
|
||||
if channel < self.channels:
|
||||
return self.amplitudeData[channel].copy()
|
||||
|
||||
def getLevelSum(self):
|
||||
if self.channels == 1:
|
||||
return self.levelData[0].copy()
|
||||
|
||||
levelSum = []
|
||||
|
||||
for f in range(self.frameCount):
|
||||
amplitudeSum = 0
|
||||
|
||||
for c in range(self.channels):
|
||||
amplitudeSum += self.levelData[c][f]
|
||||
|
||||
levelSum.append(amplitudeSum / self.channels)
|
||||
|
||||
return levelSum
|
||||
|
||||
def getCurrentLevel(self, seconds):
|
||||
frameIndex = int(round(seconds * self.frameRate))
|
||||
|
||||
if frameIndex >= self.frameCount:
|
||||
return 0
|
||||
|
||||
level = self.levelData[0][frameIndex]
|
||||
return level
|
||||
|
||||
level = 0
|
||||
|
||||
for c in range(self.channels):
|
||||
level += self.levelData[c][frameIndex]
|
||||
|
||||
return level / self.channels
|
||||
|
||||
def loadData(self):
|
||||
self.levelReset()
|
||||
self.amplitudeReset()
|
||||
soundfile = wave.open(self.filename, 'rb')
|
||||
|
||||
for f in range(self.frameCount):
|
||||
frame = soundfile.readframes(1)
|
||||
|
||||
for c in range(self.channels):
|
||||
byteOffset = c * self.sampleWidth
|
||||
amplitude = int.from_bytes(frame[byteOffset:byteOffset + self.sampleWidth], 'little')
|
||||
|
||||
if amplitude > self.amplitudeMax * 0.5:
|
||||
amplitude -= self.amplitudeMax
|
||||
|
||||
self.amplitudeData[c].append(amplitude / (self.amplitudeMax * 0.5))
|
||||
self.levelData[c].append(abs(amplitude) / (self.amplitudeMax * 0.5))
|
||||
|
||||
soundfile.close()
|
||||
|
||||
def levelReset(self):
|
||||
self.levelData = {}
|
||||
|
||||
for c in range(self.channels):
|
||||
self.levelData[c] = []
|
||||
|
||||
def amplitudeReset(self):
|
||||
self.amplitudeData = {}
|
||||
|
||||
for c in range(self.channels):
|
||||
self.amplitudeData[c] = []
|
Loading…
Reference in New Issue