#!/usr/bin/env python3 # Control Christmas-tree lights and "Wizards in Winter" (Trans-Siberian Orchestra) over Bluetooth/USB/3.5mm/HDMI (I have some lines of code commented out for using the 3.5mm jack or HDMI) # Requires "04 - Wizards in Winter.mp3" to be in the folder. # Be sure to have already done... # sudo apt-get install mpg123 # If using audio for first time since reboot, I have encountered delays or errors! # (c) 2018 Bradley Knockel # The song... # https://youtu.be/okyO1Fze7Jo # https://musescore.com/user/25698851/scores/4787304 # If using Bluetooth, you should already be paired with the speakers. # The following worked for me... # sudo apt-get install bluealsa # sudo bluetoothctl # power on # scan on # scan off <-- wait until the device shows up (device in discoverable mode) # connect 00:13:EF:00:05:17 <-- replace MAC address with one found by scanning # disconnect 00:13:EF:00:05:17 <-- wait until connected # quit # use USB speakers instead of Bluetooth? USBinstead = True USBstartSleep = 0.28 volume = 30 # between 0 and 255 # for Bluetooth speakers MACaddress=b"00:13:EF:00:05:17" startSleep = 0.77 pin1 = 21 pin2 = 20 pin3 = 16 pin4 = 12 # note that pin1 is for the TOP of the tree (pin4 is bottom) freq = 100 # for PWM # seconds for an eighth note eighth=0.2023 import RPi.GPIO as IO IO.setmode(IO.BCM) IO.setup(pin1, IO.OUT) IO.setup(pin2, IO.OUT) IO.setup(pin3, IO.OUT) IO.setup(pin4, IO.OUT) def write1(b): IO.output(pin1, b) def write2(b): IO.output(pin2, b) def write3(b): IO.output(pin3, b) def write4(b): IO.output(pin4, b) def writeAll(b): IO.output(pin1, b) IO.output(pin2, b) IO.output(pin3, b) IO.output(pin4, b) t=1 f=0 writeAll(f) import time def blink(c): a=(c-time.time())/2 writeAll(t) time.sleep(c-a-time.time()) writeAll(f) time.sleep(c-time.time()) # Does not care about previous LED state. # Leaves 2,4 on def alternate(c): a=(c-time.time())/2 write1(t) write2(f) write3(t) write4(f) time.sleep(c-a-time.time()) write1(f) write2(t) write3(f) write4(t) time.sleep(c-time.time()) def towardsCenter(c): a=(c-time.time())/4 write1(t) write4(t) time.sleep(c-3*a-time.time()) write2(t) write3(t) time.sleep(c-2*a-time.time()) write1(f) write4(f) time.sleep(c-a-time.time()) write2(f) write3(f) time.sleep(c-time.time()) def shrinkTowardsTop(c): a=(c-time.time())/4 writeAll(t) time.sleep(c-3*a-time.time()) write4(f) time.sleep(c-2*a-time.time()) write3(f) time.sleep(c-a-time.time()) write2(f) time.sleep(c-a/2-time.time()) write1(f) time.sleep(c-time.time()) def moveUpAndDown(c): a=(c-time.time())/6 # awkward timing write4(t) time.sleep(c-5*a-time.time()) write4(f) write3(t) time.sleep(c-4*a-time.time()) write3(f) write2(t) time.sleep(c-3*a-time.time()) write2(f) write1(t) time.sleep(c-2*a-time.time()) write2(t) write1(f) time.sleep(c-a-time.time()) write3(t) write2(f) time.sleep(c-time.time()) write3(f) def moveFromCenter(c): a=(c-time.time())/5 write2(t) write3(t) time.sleep(c-3*a-time.time()) write2(f) write3(f) write1(t) write4(t) time.sleep(c-a-time.time()) write1(f) write4(f) time.sleep(c-time.time()) def moveFromCenter2(c): a=(c-time.time())/4 writeAll(t) time.sleep(c-3*a-time.time()) write1(f) write4(f) time.sleep(c-2*a-time.time()) write2(f) write3(f) write1(t) write4(t) time.sleep(c-a-time.time()) write1(f) write4(f) time.sleep(c-time.time()) # leaves all LEDs on def towardsCenter2(c): a=(c-time.time())/5 write1(t) time.sleep(c-4*a-time.time()) write4(t) time.sleep(c-3*a-time.time()) write2(t) time.sleep(c-2*a-time.time()) write3(t) time.sleep(c-time.time()) def flowUpwards(c): a=(c-time.time())/4 write4(t) time.sleep(c-3*a-time.time()) write4(f) write3(t) time.sleep(c-2*a-time.time()) write3(f) write2(t) time.sleep(c-a-time.time()) write2(f) write1(t) time.sleep(c-time.time()) write1(f) # YouTube tree did this as moveFromCenter, # but I don't have enough LED sections def moveUpwards(c): a=(c-time.time())/6 write4(t) time.sleep(c-5*a-time.time()) write4(f) write3(t) time.sleep(c-4*a-time.time()) write3(f) write2(t) time.sleep(c-2*a-time.time()) write2(f) write1(t) time.sleep(c-time.time()) write1(f) # leaves all LEDs on def growUpwards(c): a=(c-time.time())/4 write4(t) time.sleep(c-3*a-time.time()) write3(t) time.sleep(c-2*a-time.time()) write2(t) time.sleep(c-a-time.time()) write1(f) time.sleep(c-time.time()) # leaves rows 1 and 3 lit def moveUpwardsDouble(c): a=(c-time.time())/8 writeAll(t) time.sleep(c-7*a-time.time()) writeAll(f) write4(t) time.sleep(c-6*a-time.time()) write4(f) write3(t) time.sleep(c-4*a-time.time()) write3(f) write4(t) write2(t) time.sleep(c-2*a-time.time()) write2(f) write4(f) write1(t) write3(t) time.sleep(c-time.time()) def fallWithStyle(c): a=(c-time.time())/4 write1(t) time.sleep(c-3*a-time.time()) write1(f) write3(t) time.sleep(c-2*a-time.time()) write3(f) write2(t) time.sleep(c-a-time.time()) write2(f) write4(t) time.sleep(c-time.time()) write4(f) import subprocess if USBinstead: ## get some info #subprocess.call(["aplay", "-l"]) #subprocess.call(["amixer", "-c0", "contents"]) #subprocess.call(["amixer", "-c1", "contents"]) ## the following lines are for using the 3.5mm jack or HDMI #subprocess.call(["amixer", "-c0", "cset", "numid=1", str(volume)]) #proc1 = subprocess.Popen(["mpg123", "--no-control", "-a", "plughw:0", "04 - Wizards in Winter.mp3"]) subprocess.call(["amixer", "-c1", "cset", "numid=3", str(volume)]) #time.sleep(10) # to fix a bug with running "mpg123 -a" right away proc1 = subprocess.Popen(["mpg123", "--no-control", "-a", "plughw:1", "04 - Wizards in Winter.mp3"]) time.sleep(USBstartSleep) else: # start Bluetooth! proc0 = subprocess.Popen(["sudo", "bluetoothctl"], bufsize=0, stdin=subprocess.PIPE) time.sleep(1) proc0.stdin.write(b'connect '+MACaddress+b'\n') time.sleep(10) proc1 = subprocess.Popen([b"mpg123", b"--no-control", b"-a", b"bluealsa:HCI=hci0,DEV="+MACaddress+b",PROFILE=a2dp", b"04 - Wizards in Winter.mp3"], bufsize=0, stdin=subprocess.PIPE) time.sleep(startSleep) try: timee = time.time() timee += eighth*3*8 towardsCenter(timee) timee += eighth*8 shrinkTowardsTop(timee) timee += eighth*3*8 towardsCenter(timee) timee += eighth*8 shrinkTowardsTop(timee) timee += eighth*4 moveUpAndDown(timee) timee += eighth*4 moveUpAndDown(timee) timee += eighth*4 moveUpAndDown(timee) timee += eighth*4 moveUpAndDown(timee) timee += eighth*4 moveUpAndDown(timee) timee += eighth*4 moveUpAndDown(timee) timee += eighth*8 shrinkTowardsTop(timee) for i in range(6): # at measure 13 timee += eighth*4 moveUpAndDown(timee) timee += eighth*2 blink(timee) timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 moveFromCenter(timee) timee += eighth*2 blink(timee) timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 moveFromCenter(timee) timee += eighth*2 blink(timee) # start measure 18 timee += eighth;time.sleep(timee-time.time()) timee += eighth*2 alternate(timee) timee += eighth*2;time.sleep(timee-time.time()) timee += eighth*2 alternate(timee) timee += eighth;time.sleep(timee-time.time()) timee += eighth*4 alternate(timee) write1(t);write2(f);write3(t);write4(f);timee += eighth;time.sleep(timee-time.time()) writeAll(f);timee += eighth;time.sleep(eighth) for i in range(6): # at measure 20 timee += eighth*4 moveUpAndDown(timee) timee += eighth*2 blink(timee ) timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 moveFromCenter(timee) for i in range(6): # at measure 24 timee += eighth*4 moveUpAndDown(timee) timee += eighth*2 blink(timee) timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 moveFromCenter(timee) timee += eighth*2 blink(timee) #start measure 28 timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 moveFromCenter(timee) timee += eighth*2 blink(timee) # start measure 29 timee += eighth;time.sleep(timee-time.time()) timee += eighth*2 alternate(timee) timee += eighth*2;time.sleep(timee-time.time()) write1(t);write2(f);write3(t);write4(f);timee += eighth;time.sleep(timee-time.time()) timee += eighth*16 shrinkTowardsTop(timee) # measures 30 and 31 # measures 32, 33, 34, 35 for i in range(16): timee += eighth*2 flowUpwards(timee) # 1:02 in the YouTube video for i in range(3): timee += eighth*2;time.sleep(timee-time.time()) timee += eighth*6 moveUpwards(timee) timee += eighth*8 flowUpwards(timee) # measure 39 for i in range(3): timee += eighth*2;time.sleep(timee-time.time()) timee += eighth*6 moveUpwards(timee) timee += eighth*8 flowUpwards(timee) # measure 43 timee += eighth*2 blink(timee) timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 moveFromCenter(timee) timee += eighth*2 blink(timee) timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 moveFromCenter(timee) timee += eighth*2 blink(timee) # start measure 46 timee += eighth;time.sleep(timee-time.time()) timee += eighth*2 alternate(timee) timee += eighth*2;time.sleep(timee-time.time()) timee += eighth*2 alternate(timee) timee += eighth;time.sleep(timee-time.time()) timee += eighth*4 alternate(timee) write1(t);write2(f);write3(t);write4(f);timee += eighth;time.sleep(timee-time.time()) writeAll(f);timee += eighth;time.sleep(timee-time.time()) for i in range(6): # at measure 48 timee += eighth*4 moveUpAndDown(timee) timee += eighth*8 shrinkTowardsTop(timee) for i in range(6): timee += eighth*4 moveUpAndDown(timee) timee += eighth*2 blink(timee) timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 moveFromCenter(timee) # end at 1:34 in YouTube timee += eighth*2 blink(timee) #start measure 56 timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 moveFromCenter(timee) timee += eighth*2 blink(timee) # start measure 57 timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 towardsCenter2(timee) timee += eighth*16 shrinkTowardsTop(timee) # measures 58 and 59 timee += eighth*8 growUpwards(timee) #measure 60 timee += eighth*8 shrinkTowardsTop(timee) #measure 61 for i in range(32): timee += eighth blink(timee) timee += eighth*2 blink(timee) timee += eighth blink(timee) # at measure 78 (at 2:10 in YouTube video) timee += eighth*2 blink(timee) timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 moveFromCenter(timee) timee += eighth*2 blink(timee) timee += eighth;time.sleep(timee-time.time()) timee += eighth*5 moveFromCenter(timee) timee += eighth*2 blink(timee) # start measure 80 timee += eighth;time.sleep(timee-time.time()) timee += eighth*2 alternate(timee) timee += eighth*2;time.sleep(timee-time.time()) timee += eighth*2 alternate(timee) timee += eighth;time.sleep(timee-time.time()) timee += eighth*4 alternate(timee) write1(t);write2(f);write3(t);write4(f);timee += eighth;time.sleep(timee-time.time()) writeAll(f);timee += eighth;time.sleep(timee-time.time()) for i in range(6): # at measure 82 timee += eighth*4 moveUpAndDown(timee) timee += eighth*8 shrinkTowardsTop(timee) for i in range(6): # at measure 13 timee += eighth*4 moveUpAndDown(timee) timee += eighth*8 moveUpwardsDouble(timee) # measure 89 (at 2:28 in YouTube video) write2(t);write4(t);timee += eighth*2;time.sleep(timee-time.time()) # measure 90 for i in range(3): timee += eighth*2 alternate(timee) write3(t);write1(t);timee += eighth/2;time.sleep(timee-time.time()) # start measure 91 write1(f);write2(t);write3(f);write4(t);timee += eighth/2;time.sleep(timee-time.time()) for i in range(3): timee += eighth alternate(timee) write3(t);write1(t);timee += eighth/2;time.sleep(timee-time.time()) write1(f);write2(t);write3(f);write4(t);timee += eighth/2;time.sleep(timee-time.time()) for i in range(3): timee += eighth alternate(timee) for i in range(3): # at measure 92 timee += eighth*2 moveUpAndDown(timee) timee += eighth*2 shrinkTowardsTop(timee) #measures 93,94,95,96,97,98,99,100 for i in range(16): timee += eighth*4 moveUpAndDown(timee) # at 2:47 in YouTube video for i in range(4): timee += eighth*4 fallWithStyle(timee) for i in range(32): timee += eighth blink(timee) # at measure 107 (at 2:57 in YouTube video) timee += eighth*4 shrinkTowardsTop(timee) timee += eighth*4 moveFromCenter2(timee) timee += eighth*4 moveUpwardsDouble(timee) timee += eighth*2 blink(timee) timee += eighth*2 alternate(timee) timee += eighth*4 shrinkTowardsTop(timee) timee += eighth*4 moveFromCenter2(timee) timee += eighth*4 moveUpwardsDouble(timee) timee += eighth*2 blink(timee) timee += eighth*2 alternate(timee) # I guess the sheet sheet music leaves out 2 measures! # measures 109 and 110 of sheet music for i in range(8): timee += eighth*2 flowUpwards(timee) # measure 111 of sheet music timee += eighth*4 blink(timee) except: pass ## cleanup GPIO IO.cleanup() ## cleanup audio time.sleep(4) proc1.terminate() if not USBinstead: # for Bluetooth time.sleep(1) proc0.stdin.write(b'disconnect '+MACaddress+b'\n'+b'quit\n') proc0.stdin.close()