Skip to content

Commit 1b18cfc

Browse files
authored
Merge pull request #1056 from firepixie/master
Burning Fire Wizard Staff Code
2 parents 1a6af0c + 1891d05 commit 1b18cfc

File tree

12 files changed

+277
-0
lines changed

12 files changed

+277
-0
lines changed

Burning_Fire_Wizard_Staff/README.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Burning_Fire_Wizard_Staff
2+
3+
Code to accompany this tutorial:
4+
https://learn.adafruit.com/burning-fire-wizard-staff
5+
6+
Prop-Maker based Burning Wizard Staff
7+
Adafruit invests time and resources providing this open source code.
8+
Please support Adafruit and open source hardware by purchasing
9+
products from Adafruit!
10+
Written by Kattni Rembor, Erin St Blaine & Limor Fried for Adafruit Industries
11+
Copyright (c) 2020 Adafruit Industries
12+
Licensed under the MIT license.
13+
All text above must be included in any redistribution.

Burning_Fire_Wizard_Staff/code.py

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
"""
2+
Prop-Maker based Burning Wizard Staff
3+
Adafruit invests time and resources providing this open source code.
4+
Please support Adafruit and open source hardware by purchasing
5+
products from Adafruit!
6+
Written by Kattni Rembor, Erin St Blaine & Limor Fried for Adafruit Industries
7+
Copyright (c) 2020 Adafruit Industries
8+
Licensed under the MIT license.
9+
All text above must be included in any redistribution.
10+
"""
11+
12+
import time
13+
import random
14+
import digitalio
15+
import audioio
16+
import audiocore
17+
import board
18+
import neopixel
19+
import adafruit_lis3dh
20+
21+
# CHANGE TO MATCH YOUR RING AND STRIP SETUP
22+
NUM_RING = 12 #12 pixel ring
23+
NUM_STRIP = 44 # 44 pixels in my NeoPixel strip
24+
NUM_PIXELS = NUM_STRIP + NUM_RING #total number of pixels
25+
26+
NEOPIXEL_PIN = board.D5 # PropMaker Wing uses D5 for NeoPixel plug
27+
POWER_PIN = board.D10
28+
29+
30+
# CUSTOMISE COLORS HERE:
31+
COLOR = (200, 30, 0) # Default idle is orange
32+
ALT_COLOR = (0, 200, 200) # hit color is teal
33+
SWING_COLOR = (200, 200, 200) #swing animation color is white
34+
TOP_COLOR = (100, 100, 0) #top color is yellow-green
35+
YELL_COLOR = (200, 0, 200) #yell color is purple
36+
37+
# CUSTOMISE IDLE PULSE SPEED HERE: 0 is fast, above 0 slows down
38+
IDLE_PULSE_SPEED = 0 # Default is 0 seconds
39+
SWING_BLAST_SPEED = 0.007
40+
41+
# CUSTOMISE BRIGHTNESS HERE: must be a number between 0 and 1
42+
IDLE_PULSE_BRIGHTNESS_MIN = 0.2 # Default minimum idle pulse brightness
43+
IDLE_PULSE_BRIGHTNESS_MAX = 1 # Default maximum idle pulse brightness
44+
45+
# CUSTOMISE SENSITIVITY HERE: smaller numbers = more sensitive to motion
46+
HIT_THRESHOLD = 1150
47+
SWING_THRESHOLD = 800
48+
YELL_THRESHOLD = 700
49+
50+
# Set to the length in seconds of the "on.wav" and "yell1.wav" files
51+
POWER_ON_SOUND_DURATION = 3.0
52+
YELL_SOUND_DURATION = 1.0
53+
54+
55+
enable = digitalio.DigitalInOut(POWER_PIN)
56+
enable.direction = digitalio.Direction.OUTPUT
57+
enable.value = False
58+
59+
# Set up NeoPixels
60+
strip = neopixel.NeoPixel(NEOPIXEL_PIN, NUM_PIXELS, brightness=1, auto_write=False)
61+
strip.fill(0) # NeoPixels off ASAP on startup
62+
strip.show()
63+
64+
audio = audioio.AudioOut(board.A0) # Speaker
65+
wave_file = None
66+
67+
# Set up accelerometer on I2C bus, 4G range:
68+
i2c = board.I2C()
69+
accel = adafruit_lis3dh.LIS3DH_I2C(i2c)
70+
accel.range = adafruit_lis3dh.RANGE_4_G
71+
72+
COLOR_IDLE = COLOR # 'idle' color is the default for the staff handle
73+
COLOR_HIT = ALT_COLOR # "hit" color is ALT_COLOR set above
74+
COLOR_SWING = SWING_COLOR # "swing" color is SWING_COLOR set above
75+
COLOR_TOP = TOP_COLOR #"top" color is idle color for the ring
76+
77+
78+
def play_wav(name, loop=False):
79+
"""
80+
Play a WAV file in the 'sounds' directory.
81+
:param name: partial file name string, complete name will be built around
82+
this, e.g. passing 'foo' will play file 'sounds/foo.wav'.
83+
:param loop: if True, sound will repeat indefinitely (until interrupted
84+
by another sound).
85+
"""
86+
global wave_file # pylint: disable=global-statement
87+
print("playing", name)
88+
if wave_file:
89+
wave_file.close()
90+
try:
91+
wave_file = open('sounds/' + name + '.wav', 'rb')
92+
wave = audiocore.WaveFile(wave_file)
93+
audio.play(wave, loop=loop)
94+
except OSError:
95+
pass # we'll just skip playing then
96+
97+
98+
def power(sound, duration, reverse):
99+
"""
100+
Animate NeoPixels with accompanying sound effect for power on.
101+
@param sound: sound name (similar format to play_wav() above)
102+
@param duration: estimated duration of sound, in seconds (>0.0)
103+
@param reverse: Reverses animation. If True, begins animation at end of strip.
104+
"""
105+
if reverse:
106+
prev = NUM_PIXELS
107+
else:
108+
prev = 0
109+
start_time = time.monotonic() # Save audio start time
110+
play_wav(sound)
111+
while True:
112+
elapsed = time.monotonic() - start_time # Time spent playing sound
113+
if elapsed > duration: # Past sound duration?
114+
break # Stop animating
115+
total_animation_time = elapsed / duration # Animation time, 0.0 to 1.0
116+
if reverse:
117+
total_animation_time = 1.0 - total_animation_time # 1.0 to 0.0 if reverse
118+
threshold = int(NUM_PIXELS * total_animation_time + 0.5)
119+
num = threshold - prev # Number of pixels to light on this pass
120+
if num != 0:
121+
if reverse:
122+
strip[threshold:prev] = [ALT_COLOR] * -num
123+
else:
124+
strip[prev:threshold] = [ALT_COLOR] * num
125+
strip.show()
126+
prev = threshold
127+
128+
129+
def mix(color_1, color_2, weight_2):
130+
"""
131+
Blend between two colors with a given ratio.
132+
:param color_1: first color, as an (r,g,b) tuple
133+
:param color_2: second color, as an (r,g,b) tuple
134+
:param weight_2: Blend weight (ratio) of second color, 0.0 to 1.0
135+
:return (r,g,b) tuple, blended color
136+
"""
137+
if weight_2 < 0.0:
138+
weight_2 = 0.0
139+
elif weight_2 > 1.0:
140+
weight_2 = 1.0
141+
weight_1 = 1.0 - weight_2
142+
return (int(color_1[0] * weight_1 + color_2[0] * weight_2),
143+
int(color_1[1] * weight_1 + color_2[1] * weight_2),
144+
int(color_1[2] * weight_1 + color_2[2] * weight_2))
145+
146+
# List of swing wav files without the .wav in the name for use with play_wav()
147+
swing_sounds = [
148+
'swing1',
149+
'swing2',
150+
'swing3',
151+
]
152+
153+
# List of hit wav files without the .wav in the name for use with play_wav()
154+
hit_sounds = [
155+
'hit1',
156+
'hit2',
157+
'hit3',
158+
'hit4',
159+
]
160+
161+
# List of yell wav files without the .wav in the name for use with play_wav()
162+
yell_sounds = [
163+
'yell1',
164+
]
165+
166+
167+
mode = 0 # Initial mode = OFF
168+
169+
# Setup idle pulse
170+
idle_brightness = IDLE_PULSE_BRIGHTNESS_MIN # current brightness of idle pulse
171+
idle_increment = 0.01 # Initial idle pulse direction
172+
173+
# Main loop
174+
while True:
175+
176+
if mode == 0: # If currently off...
177+
enable.value = True
178+
power('on', POWER_ON_SOUND_DURATION, True) # Power up!
179+
play_wav('idle', loop=True) # Play idle sound now
180+
mode = 1 # Idle mode
181+
time.sleep(1.0) #pause before moving on
182+
183+
# Setup for idle pulse
184+
idle_brightness = IDLE_PULSE_BRIGHTNESS_MIN
185+
idle_increment = 0.01
186+
# lights the ring in COLOR_TOP color:
187+
strip[0:NUM_RING] = [([int(c*idle_brightness) for c in COLOR_TOP])] * NUM_RING
188+
# lights the strip in COLOR_IDLE color:
189+
strip[NUM_RING:NUM_PIXELS] = [([int(c*idle_brightness) for c in COLOR_IDLE])] * NUM_STRIP
190+
strip.show()
191+
192+
elif mode >= 1: # If not OFF mode...
193+
x, y, z = accel.acceleration # Read accelerometer
194+
accel_total = x * x + z * z #x axis used for hit and for swing
195+
accel_yell = y * y + z * z #y axis used for yell
196+
# Square root isn't needed, since we're
197+
# comparing thresholds...use squared values instead.)
198+
if accel_total > HIT_THRESHOLD: # Large acceleration on x axis = HIT
199+
TRIGGER_TIME = time.monotonic() # Save initial time of hit
200+
play_wav(random.choice(hit_sounds)) # Start playing 'hit' sound
201+
COLOR_ACTIVE = COLOR_HIT # Set color to fade from
202+
mode = 3 # HIT mode
203+
elif mode == 1 and accel_total > SWING_THRESHOLD: # Mild acceleration on x axis = SWING
204+
TRIGGER_TIME = time.monotonic() # Save initial time of swing
205+
play_wav(random.choice(swing_sounds)) # Randomly choose from available swing sounds
206+
# make a larson scanner
207+
strip_backup = strip[0:-1]
208+
for p in range(-1, len(strip)):
209+
for i in range(p-1, p+2): # shoot a 'ray' of 3 pixels
210+
if 0 <= i < len(strip):
211+
strip[i] = COLOR_SWING
212+
strip.show()
213+
time.sleep(SWING_BLAST_SPEED)
214+
if 0 <= (p-1) < len(strip):
215+
strip[p-1] = strip_backup[p-1] # restore previous color at the tail
216+
strip.show()
217+
while audio.playing:
218+
pass # wait till we're done
219+
mode = 2 # we'll go back to idle mode
220+
elif mode == 1 and accel_yell > YELL_THRESHOLD: # Motion on Y axis = YELL
221+
TRIGGER_TIME = time.monotonic() # Save initial time of swing
222+
# run a color down the staff, opposite of power-up
223+
previous = 0
224+
audio_start_time = time.monotonic() # Save audio start time
225+
play_wav(random.choice(yell_sounds)) # Randomly choose from available yell sounds
226+
sound_duration = YELL_SOUND_DURATION
227+
while True:
228+
time_elapsed = time.monotonic() - audio_start_time # Time spent playing sound
229+
if time_elapsed > sound_duration: # Past sound duration?
230+
break # Stop animating
231+
animation_time = time_elapsed / sound_duration # Animation time, 0.0 to 1.0
232+
pixel_threshold = int(NUM_PIXELS * animation_time + 0.5)
233+
num_pixels = pixel_threshold - previous # Number of pixels to light on this pass
234+
if num_pixels != 0:
235+
# light pixels in YELL_COLOR:
236+
strip[previous:pixel_threshold] = [YELL_COLOR] * num_pixels
237+
strip.show()
238+
previous = pixel_threshold
239+
while audio.playing:
240+
pass # wait till we're done
241+
mode = 4 # we'll go back to idle mode
242+
elif mode == 1:
243+
# Idle pulse
244+
idle_brightness += idle_increment # Pulse up
245+
if idle_brightness > IDLE_PULSE_BRIGHTNESS_MAX or \
246+
idle_brightness < IDLE_PULSE_BRIGHTNESS_MIN: # Then...
247+
idle_increment *= -1 # Pulse direction flip
248+
# light the ring:
249+
strip[0:NUM_RING] = [([int(c*idle_brightness) for c in COLOR_TOP])] * NUM_RING
250+
# light the strip:
251+
strip[NUM_RING:NUM_PIXELS] = [([int(c*idle_brightness) for c in
252+
COLOR_IDLE])] * NUM_STRIP
253+
strip.show()
254+
time.sleep(IDLE_PULSE_SPEED) # Idle pulse speed set above
255+
elif mode > 1: # If in SWING or HIT or YELL mode...
256+
if audio.playing: # And sound currently playing...
257+
blend = time.monotonic() - TRIGGER_TIME # Time since triggered
258+
if mode == 2: # If SWING,
259+
blend = abs(0.5 - blend) * 3.0 # ramp up, down
260+
strip.fill(mix(COLOR_ACTIVE, COLOR, blend)) # Fade from hit/swing to base color
261+
strip.show()
262+
else: # No sound now, but still SWING or HIT modes
263+
play_wav('idle', loop=True) # Resume idle sound
264+
mode = 1 # Return to idle mode
52.7 KB
Binary file not shown.
86.4 KB
Binary file not shown.
71.6 KB
Binary file not shown.
52.7 KB
Binary file not shown.
264 KB
Binary file not shown.
46.9 KB
Binary file not shown.
7.94 KB
Binary file not shown.
9 KB
Binary file not shown.
9.17 KB
Binary file not shown.
31.3 KB
Binary file not shown.

0 commit comments

Comments
 (0)