Skip to content

Commit 40bd8bc

Browse files
committed
Adding master sword code and sounds
Adding code for the prop-maker feather master sword project
1 parent e24599c commit 40bd8bc

File tree

11 files changed

+215
-0
lines changed

11 files changed

+215
-0
lines changed
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
2+
# SPDX-FileCopyrightText: 2019 Limor Fried for Adafruit Industries
3+
#
4+
# SPDX-License-Identifier: MIT
5+
6+
"""
7+
RP2040 Prop-Maker Feather Master Sword
8+
Adafruit invests time and resources providing this open source code.
9+
Please support Adafruit and open source hardware by purchasing
10+
products from Adafruit!
11+
Written by Kattni Rembor & Limor Fried for Adafruit Industries
12+
Copyright (c) 2019-2020 Adafruit Industries
13+
Licensed under the MIT license.
14+
All text above must be included in any redistribution.
15+
"""
16+
17+
import time
18+
import random
19+
import digitalio
20+
import audiocore
21+
import audiobusio
22+
import board
23+
import neopixel
24+
import adafruit_lis3dh
25+
26+
# CUSTOMISE COLORS HERE:
27+
COLOR = (0, 120, 120) # Default idle is light blue
28+
ALT_COLOR = (255, 50, 0) # hit color is orange
29+
30+
# CUSTOMISE IDLE PULSE SPEED HERE: 0 is fast, above 0 slows down
31+
IDLE_PULSE_SPEED = 0 # Default is 0 seconds
32+
SWING_BLAST_SPEED = 0.0007
33+
34+
# CUSTOMISE BRIGHTNESS HERE: must be a number between 0 and 1
35+
IDLE_PULSE_BRIGHTNESS_MIN = 0.2 # Default minimum idle pulse brightness
36+
IDLE_PULSE_BRIGHTNESS_MAX = 1 # Default maximum idle pulse brightness
37+
38+
# CUSTOMISE SENSITIVITY HERE: smaller numbers = more sensitive to motion
39+
HIT_THRESHOLD = 250
40+
SWING_THRESHOLD = 150
41+
42+
# Set to the length in seconds of the "on.wav" file
43+
POWER_ON_SOUND_DURATION = 1.7
44+
45+
NUM_PIXELS = 73 # Number of pixels used in project
46+
NEOPIXEL_PIN = board.EXTERNAL_NEOPIXELS
47+
48+
enable = digitalio.DigitalInOut(board.EXTERNAL_POWER)
49+
enable.direction = digitalio.Direction.OUTPUT
50+
enable.value = True
51+
52+
strip = neopixel.NeoPixel(NEOPIXEL_PIN, NUM_PIXELS, brightness=1, auto_write=False)
53+
strip.fill(0) # NeoPixels off ASAP on startup
54+
strip.show()
55+
56+
# i2s audio
57+
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
58+
wave_file = None
59+
60+
# Set up accelerometer on I2C bus, 4G range:
61+
i2c = board.I2C() # uses board.SCL and board.SDA
62+
# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller
63+
accel = adafruit_lis3dh.LIS3DH_I2C(i2c)
64+
accel.range = adafruit_lis3dh.RANGE_4_G
65+
66+
COLOR_IDLE = COLOR # 'idle' color is the default
67+
COLOR_HIT = ALT_COLOR # "hit" color is ALT_COLOR set above
68+
COLOR_SWING = ALT_COLOR # "swing" color is ALT_COLOR set above
69+
70+
71+
def play_wav(name, loop=False):
72+
"""
73+
Play a WAV file in the 'sounds' directory.
74+
:param name: partial file name string, complete name will be built around
75+
this, e.g. passing 'foo' will play file 'sounds/foo.wav'.
76+
:param loop: if True, sound will repeat indefinitely (until interrupted
77+
by another sound).
78+
"""
79+
global wave_file # pylint: disable=global-statement
80+
print("playing", name)
81+
if wave_file:
82+
wave_file.close()
83+
try:
84+
wave_file = open('sounds/' + name + '.wav', 'rb')
85+
wave = audiocore.WaveFile(wave_file)
86+
audio.play(wave, loop=loop)
87+
except OSError:
88+
pass # we'll just skip playing then
89+
90+
91+
def power_on(sound, duration):
92+
"""
93+
Animate NeoPixels with accompanying sound effect for power on.
94+
:param sound: sound name (similar format to play_wav() above)
95+
:param duration: estimated duration of sound, in seconds (>0.0)
96+
"""
97+
prev = 0
98+
start_time = time.monotonic() # Save audio start time
99+
play_wav(sound)
100+
while True:
101+
elapsed = time.monotonic() - start_time # Time spent playing sound
102+
if elapsed > duration: # Past sound duration?
103+
break # Stop animating
104+
animation_time = elapsed / duration # Animation time, 0.0 to 1.0
105+
threshold = int(NUM_PIXELS * animation_time + 0.5)
106+
num = threshold - prev # Number of pixels to light on this pass
107+
if num != 0:
108+
strip[prev:threshold] = [ALT_COLOR] * num
109+
strip.show()
110+
prev = threshold
111+
112+
113+
def mix(color_1, color_2, weight_2):
114+
"""
115+
Blend between two colors with a given ratio.
116+
:param color_1: first color, as an (r,g,b) tuple
117+
:param color_2: second color, as an (r,g,b) tuple
118+
:param weight_2: Blend weight (ratio) of second color, 0.0 to 1.0
119+
:return (r,g,b) tuple, blended color
120+
"""
121+
if weight_2 < 0.0:
122+
weight_2 = 0.0
123+
elif weight_2 > 1.0:
124+
weight_2 = 1.0
125+
weight_1 = 1.0 - weight_2
126+
return (int(color_1[0] * weight_1 + color_2[0] * weight_2),
127+
int(color_1[1] * weight_1 + color_2[1] * weight_2),
128+
int(color_1[2] * weight_1 + color_2[2] * weight_2))
129+
130+
# List of swing wav files without the .wav in the name for use with play_wav()
131+
swing_sounds = [
132+
'swing1',
133+
'swing2',
134+
'swing3',
135+
'swing4',
136+
]
137+
138+
# List of hit wav files without the .wav in the name for use with play_wav()
139+
hit_sounds = [
140+
'hit1',
141+
'hit2',
142+
'hit3',
143+
'hit4',
144+
]
145+
146+
147+
mode = 0 # Initial mode = OFF
148+
149+
# Setup idle pulse
150+
idle_brightness = IDLE_PULSE_BRIGHTNESS_MIN # current brightness of idle pulse
151+
idle_increment = 0.01 # Initial idle pulse direction
152+
153+
# Main loop
154+
while True:
155+
156+
if mode == 0: # If currently off...
157+
enable.value = True
158+
power_on('on', POWER_ON_SOUND_DURATION) # Power up!
159+
play_wav('idle', loop=True) # Play idle sound now
160+
mode = 1 # Idle mode
161+
162+
# Setup for idle pulse
163+
idle_brightness = IDLE_PULSE_BRIGHTNESS_MIN
164+
idle_increment = 0.01
165+
strip.fill([int(c*idle_brightness) for c in COLOR])
166+
strip.show()
167+
168+
elif mode >= 1: # If not OFF mode...
169+
x, y, z = accel.acceleration # Read accelerometer
170+
accel_total = x * x + z * z
171+
# (Y axis isn't needed, due to the orientation that the Prop-Maker
172+
# Wing is mounted. Also, square root isn't needed, since we're
173+
# comparing thresholds...use squared values instead.)
174+
if accel_total > HIT_THRESHOLD: # Large acceleration = HIT
175+
TRIGGER_TIME = time.monotonic() # Save initial time of hit
176+
play_wav(random.choice(hit_sounds)) # Start playing 'hit' sound
177+
COLOR_ACTIVE = COLOR_HIT # Set color to fade from
178+
mode = 3 # HIT mode
179+
elif mode == 1 and accel_total > SWING_THRESHOLD: # Mild = SWING
180+
TRIGGER_TIME = time.monotonic() # Save initial time of swing
181+
play_wav(random.choice(swing_sounds)) # Randomly choose from available swing sounds
182+
# make a larson scanner animation_time
183+
strip_backup = strip[0:-1]
184+
for p in range(-1, len(strip)):
185+
for i in range (p-1, p+2): # shoot a 'ray' of 3 pixels
186+
if 0 <= i < len(strip):
187+
strip[i] = COLOR_SWING
188+
strip.show()
189+
time.sleep(SWING_BLAST_SPEED)
190+
if 0 <= (p-1) < len(strip):
191+
strip[p-1] = strip_backup[p-1] # restore previous color at the tail
192+
strip.show()
193+
while audio.playing:
194+
pass # wait till we're done
195+
mode = 2 # we'll go back to idle mode
196+
197+
elif mode == 1:
198+
# Idle pulse
199+
idle_brightness += idle_increment # Pulse up
200+
if idle_brightness > IDLE_PULSE_BRIGHTNESS_MAX or \
201+
idle_brightness < IDLE_PULSE_BRIGHTNESS_MIN: # Then...
202+
idle_increment *= -1 # Pulse direction flip
203+
strip.fill([int(c*idle_brightness) for c in COLOR_IDLE])
204+
strip.show()
205+
time.sleep(IDLE_PULSE_SPEED) # Idle pulse speed set above
206+
elif mode > 1: # If in SWING or HIT mode...
207+
if audio.playing: # And sound currently playing...
208+
blend = time.monotonic() - TRIGGER_TIME # Time since triggered
209+
if mode == 2: # If SWING,
210+
blend = abs(0.5 - blend) * 2.0 # ramp up, down
211+
strip.fill(mix(COLOR_ACTIVE, COLOR, blend)) # Fade from hit/swing to base color
212+
strip.show()
213+
else: # No sound now, but still SWING or HIT modes
214+
play_wav('idle', loop=True) # Resume idle sound
215+
mode = 1 # Return to idle mode
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)