Skip to content

Commit 7096244

Browse files
authored
Merge branch 'master' into master
2 parents e7b722f + 88b1be2 commit 7096244

File tree

1 file changed

+187
-0
lines changed

1 file changed

+187
-0
lines changed

PropMaker_Lightsaber/code.py

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
"""LASER SWORD (pew pew) example for Adafruit Hallowing & NeoPixel strip"""
2+
# pylint: disable=bare-except
3+
4+
import time
5+
import math
6+
import gc
7+
from digitalio import DigitalInOut, Direction, Pull
8+
import audioio
9+
import busio
10+
import board
11+
import neopixel
12+
import adafruit_lis3dh
13+
14+
# CUSTOMIZE YOUR COLOR HERE:
15+
# (red, green, blue) -- each 0 (off) to 255 (brightest)
16+
# COLOR = (255, 0, 0) # red
17+
COLOR = (100, 0, 255) # purple
18+
# COLOR = (0, 100, 255) #cyan
19+
20+
# CUSTOMIZE SENSITIVITY HERE: smaller numbers = more sensitive to motion
21+
HIT_THRESHOLD = 350 # 250
22+
SWING_THRESHOLD = 125
23+
24+
NUM_PIXELS = 114
25+
# NUM_PIXELS = 85
26+
NEOPIXEL_PIN = board.D5
27+
POWER_PIN = board.D10
28+
SWITCH_PIN = board.D9
29+
30+
enable = DigitalInOut(POWER_PIN)
31+
enable.direction = Direction.OUTPUT
32+
enable.value =False
33+
34+
red_led = DigitalInOut(board.D11)
35+
red_led.direction = Direction.OUTPUT
36+
green_led = DigitalInOut(board.D12)
37+
green_led.direction = Direction.OUTPUT
38+
blue_led = DigitalInOut(board.D13)
39+
blue_led.direction = Direction.OUTPUT
40+
41+
audio = audioio.AudioOut(board.A0) # Speaker
42+
mode = 0 # Initial mode = OFF
43+
44+
strip = neopixel.NeoPixel(NEOPIXEL_PIN, NUM_PIXELS, brightness=1, auto_write=False)
45+
strip.fill(0) # NeoPixels off ASAP on startup
46+
strip.show()
47+
48+
switch = DigitalInOut(SWITCH_PIN)
49+
switch.direction = Direction.INPUT
50+
switch.pull = Pull.UP
51+
52+
time.sleep(0.1)
53+
54+
# Set up accelerometer on I2C bus, 4G range:
55+
i2c = busio.I2C(board.SCL, board.SDA)
56+
accel = adafruit_lis3dh.LIS3DH_I2C(i2c)
57+
accel.range = adafruit_lis3dh.RANGE_4_G
58+
59+
# "Idle" color is 1/4 brightness, "swinging" color is full brightness...
60+
COLOR_IDLE = (int(COLOR[0] / 1), int(COLOR[1] / 1), int(COLOR[2] / 1))
61+
COLOR_SWING = COLOR
62+
COLOR_HIT = (255, 255, 255) # "hit" color is white
63+
64+
def play_wav(name, loop=False):
65+
"""
66+
Play a WAV file in the 'sounds' directory.
67+
@param name: partial file name string, complete name will be built around
68+
this, e.g. passing 'foo' will play file 'sounds/foo.wav'.
69+
@param loop: if True, sound will repeat indefinitely (until interrupted
70+
by another sound).
71+
"""
72+
print("playing", name)
73+
try:
74+
wave_file = open('sounds/' + name + '.wav', 'rb')
75+
wave = audioio.WaveFile(wave_file)
76+
audio.play(wave, loop=loop)
77+
except:
78+
return
79+
80+
def power(sound, duration, reverse):
81+
"""
82+
Animate NeoPixels with accompanying sound effect for power on / off.
83+
@param sound: sound name (similar format to play_wav() above)
84+
@param duration: estimated duration of sound, in seconds (>0.0)
85+
@param reverse: if True, do power-off effect (reverses animation)
86+
"""
87+
if reverse:
88+
prev = NUM_PIXELS
89+
else:
90+
prev = 0
91+
gc.collect() # Tidy up RAM now so animation's smoother
92+
start_time = time.monotonic() # Save audio start time
93+
play_wav(sound)
94+
while True:
95+
elapsed = time.monotonic() - start_time # Time spent playing sound
96+
if elapsed > duration: # Past sound duration?
97+
break # Stop animating
98+
fraction = elapsed / duration # Animation time, 0.0 to 1.0
99+
if reverse:
100+
fraction = 1.0 - fraction # 1.0 to 0.0 if reverse
101+
fraction = math.pow(fraction, 0.5) # Apply nonlinear curve
102+
threshold = int(NUM_PIXELS * fraction + 0.5)
103+
num = threshold - prev # Number of pixels to light on this pass
104+
if num != 0:
105+
if reverse:
106+
strip[threshold:prev] = [0] * -num
107+
else:
108+
strip[prev:threshold] = [COLOR_IDLE] * num
109+
strip.show()
110+
# NeoPixel writes throw off time.monotonic() ever so slightly
111+
# because interrupts are disabled during the transfer.
112+
# We can compensate somewhat by adjusting the start time
113+
# back by 30 microseconds per pixel.
114+
start_time -= NUM_PIXELS * 0.00003
115+
prev = threshold
116+
117+
if reverse:
118+
strip.fill(0) # At end, ensure strip is off
119+
else:
120+
strip.fill(COLOR_IDLE) # or all pixels set on
121+
strip.show()
122+
while audio.playing: # Wait until audio done
123+
pass
124+
125+
def mix(color_1, color_2, weight_2):
126+
"""
127+
Blend between two colors with a given ratio.
128+
@param color_1: first color, as an (r,g,b) tuple
129+
@param color_2: second color, as an (r,g,b) tuple
130+
@param weight_2: Blend weight (ratio) of second color, 0.0 to 1.0
131+
@return: (r,g,b) tuple, blended color
132+
"""
133+
if weight_2 < 0.0:
134+
weight_2 = 0.0
135+
elif weight_2 > 1.0:
136+
weight_2 = 1.0
137+
weight_1 = 1.0 - weight_2
138+
return (int(color_1[0] * weight_1 + color_2[0] * weight_2),
139+
int(color_1[1] * weight_1 + color_2[1] * weight_2),
140+
int(color_1[2] * weight_1 + color_2[2] * weight_2))
141+
142+
# Main program loop, repeats indefinitely
143+
while True:
144+
145+
red_led.value = True
146+
147+
if not switch.value: # button pressed?
148+
if mode == 0: # If currently off...
149+
enable.value = True
150+
power('on', 1.7, False) # Power up!
151+
play_wav('idle', loop=True) # Play background hum sound
152+
mode = 1 # ON (idle) mode now
153+
else: # else is currently on...
154+
power('off', 1.15, True) # Power down
155+
mode = 0 # OFF mode now
156+
enable.value = False
157+
while not switch.value: # Wait for button release
158+
time.sleep(0.2) # to avoid repeated triggering
159+
160+
elif mode >= 1: # If not OFF mode...
161+
x, y, z = accel.acceleration # Read accelerometer
162+
accel_total = x * x + z * z
163+
# (Y axis isn't needed for this, assuming Hallowing is mounted
164+
# sideways to stick. Also, square root isn't needed, since we're
165+
# just comparing thresholds...use squared values instead, save math.)
166+
if accel_total > HIT_THRESHOLD: # Large acceleration = HIT
167+
TRIGGER_TIME = time.monotonic() # Save initial time of hit
168+
play_wav('hit') # Start playing 'hit' sound
169+
COLOR_ACTIVE = COLOR_HIT # Set color to fade from
170+
mode = 3 # HIT mode
171+
elif mode == 1 and accel_total > SWING_THRESHOLD: # Mild = SWING
172+
TRIGGER_TIME = time.monotonic() # Save initial time of swing
173+
play_wav('swing') # Start playing 'swing' sound
174+
COLOR_ACTIVE = COLOR_SWING # Set color to fade from
175+
mode = 2 # SWING mode
176+
elif mode > 1: # If in SWING or HIT mode...
177+
if audio.playing: # And sound currently playing...
178+
blend = time.monotonic() - TRIGGER_TIME # Time since triggered
179+
if mode == 2: # If SWING,
180+
blend = abs(0.5 - blend) * 2.0 # ramp up, down
181+
strip.fill(mix(COLOR_ACTIVE, COLOR_IDLE, blend))
182+
strip.show()
183+
else: # No sound now, but still MODE > 1
184+
play_wav('idle', loop=True) # Resume background hum
185+
strip.fill(COLOR_IDLE) # Set to idle color
186+
strip.show()
187+
mode = 1 # IDLE mode now

0 commit comments

Comments
 (0)