Skip to content

Commit 48df112

Browse files
authored
Merge branch 'master' into master
2 parents 59151c7 + 6229d42 commit 48df112

File tree

2 files changed

+170
-1
lines changed

2 files changed

+170
-1
lines changed

CPB_Quick_Draw_Duo/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ The code in this repo accompanies the Adafruit tutorial
44
Circuit Playground Bluefruit Quick Draw Duo
55
https://learn.adafruit.com/circuit-playground-bluefruit-quick-draw-duo/
66

7-
The Python code file should be copied onto the **CIRCUITPY** flash drive that appears
7+
The Python code file (code.py or reaction.py) should be copied onto the **CIRCUITPY** flash drive that appears
88
when you plug the CircuitPlayground Express into your computer (via a known good USB cable) as **code.py**.
99
Also copy the wav file for sound per the tutorial.
1010

CPB_Quick_Draw_Duo/reaction.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
# cpx-reaction-timer v1.0.1
2+
# A human reaction timer using light and sound
3+
4+
# Measures the time it takes for user to press the right button
5+
# in response to alternate first NeoPixel and beeps from onboard speaker,
6+
# prints times and statistics in Mu friendly format.
7+
8+
import os
9+
import time
10+
import math
11+
import random
12+
import array
13+
import gc
14+
import board
15+
import digitalio
16+
import analogio
17+
18+
# This code works on both CPB and CPX boards by bringing
19+
# in classes with same name
20+
try:
21+
from audiocore import RawSample
22+
except ImportError:
23+
from audioio import RawSample
24+
try:
25+
from audioio import AudioOut
26+
except ImportError:
27+
from audiopwmio import PWMAudioOut as AudioOut
28+
29+
import neopixel
30+
31+
def seed_with_noise():
32+
"""Set random seed based on four reads from analogue pads.
33+
Disconnected pads on CPX produce slightly noisy 12bit ADC values.
34+
Shuffling bits around a little to distribute that noise."""
35+
a2 = analogio.AnalogIn(board.A2)
36+
a3 = analogio.AnalogIn(board.A3)
37+
a4 = analogio.AnalogIn(board.A4)
38+
a5 = analogio.AnalogIn(board.A5)
39+
random_value = ((a2.value >> 4) + (a3.value << 1) +
40+
(a4.value << 6) + (a5.value << 11))
41+
for pin in (a2, a3, a4, a5):
42+
pin.deinit()
43+
random.seed(random_value)
44+
45+
# Without os.urandom() the random library does not set a useful seed
46+
try:
47+
os.urandom(4)
48+
except NotImplementedError:
49+
seed_with_noise()
50+
51+
# Turn the speaker on
52+
speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE)
53+
speaker_enable.direction = digitalio.Direction.OUTPUT
54+
speaker_enable.value = True
55+
56+
audio = AudioOut(board.SPEAKER)
57+
58+
# Number of seconds
59+
SHORTEST_DELAY = 3.0
60+
LONGEST_DELAY = 7.0
61+
62+
red = (40, 0, 0)
63+
black = (0, 0, 0)
64+
65+
A4refhz = 440
66+
midpoint = 32768
67+
twopi = 2 * math.pi
68+
69+
def sawtooth(angle):
70+
"""A sawtooth function like math.sin(angle).
71+
Input of 0 returns 1.0, pi returns 0.0, 2*pi returns -1.0."""
72+
73+
return 1.0 - angle % twopi / twopi * 2
74+
75+
# make a sawtooth wave between +/- each value in volumes
76+
# phase shifted so it starts and ends near midpoint
77+
vol = 32767
78+
sample_len = 10
79+
waveraw = array.array("H",
80+
[midpoint +
81+
round(vol * sawtooth((idx + 0.5) / sample_len
82+
* twopi
83+
+ math.pi))
84+
for idx in range(sample_len)])
85+
86+
beep = RawSample(waveraw, sample_rate=sample_len * A4refhz)
87+
88+
# play something to get things inside audio libraries initialised
89+
audio.play(beep, loop=True)
90+
time.sleep(0.1)
91+
audio.stop()
92+
audio.play(beep)
93+
94+
# brightness 1.0 saves memory by removing need for a second buffer
95+
# 10 is number of NeoPixels on CPX/CPB
96+
numpixels = 10
97+
pixels = neopixel.NeoPixel(board.NEOPIXEL, numpixels, brightness=1.0)
98+
99+
# B is right (usb at top)
100+
button_right = digitalio.DigitalInOut(board.BUTTON_B)
101+
button_right.switch_to_input(pull=digitalio.Pull.DOWN)
102+
103+
def wait_finger_off_and_random_delay():
104+
"""Ensure finger is not touching the button then execute random delay."""
105+
while button_right.value:
106+
pass
107+
duration = (SHORTEST_DELAY +
108+
random.random() * (LONGEST_DELAY - SHORTEST_DELAY))
109+
time.sleep(duration)
110+
111+
112+
def update_stats(stats, test_type, test_num, duration):
113+
"""Update stats dict and return data in tuple for printing."""
114+
stats[test_type]["values"].append(duration)
115+
stats[test_type]["sum"] += duration
116+
stats[test_type]["mean"] = stats[test_type]["sum"] / test_num
117+
118+
if test_num > 1:
119+
# Calculate (sample) variance
120+
var_s = (sum([(x - stats[test_type]["mean"])**2
121+
for x in stats[test_type]["values"]])
122+
/ (test_num - 1))
123+
else:
124+
var_s = 0.0
125+
126+
stats[test_type]["sd_sample"] = var_s ** 0.5
127+
128+
return ("Trial " + str(test_num), test_type, duration,
129+
stats[test_type]["mean"], stats[test_type]["sd_sample"])
130+
131+
run = 1
132+
statistics = {"visual": {"values": [], "sum": 0.0, "mean": 0.0,
133+
"sd_sample": 0.0},
134+
"auditory": {"values": [], "sum": 0.0, "mean": 0.0,
135+
"sd_sample": 0.0},
136+
"tactile": {"values": [], "sum": 0.0, "mean": 0.0,
137+
"sd_sample": 0.0}}
138+
139+
print("# Trialnumber, time, mean, standarddeviation")
140+
# serial console output is printed as tuple to allow Mu to graph it
141+
while True:
142+
# Visual test using first NeoPixel
143+
wait_finger_off_and_random_delay()
144+
# do GC now to reduce likelihood of occurrence during reaction timing
145+
gc.collect()
146+
pixels[0] = red
147+
start_t = time.monotonic()
148+
while not button_right.value:
149+
pass
150+
react_t = time.monotonic()
151+
reaction_dur = react_t - start_t
152+
print(update_stats(statistics, "visual", run, reaction_dur))
153+
pixels[0] = black
154+
155+
# Auditory test using onboard speaker and 444.4Hz beep
156+
wait_finger_off_and_random_delay()
157+
# do GC now to reduce likelihood of occurrence during reaction timing
158+
gc.collect()
159+
audio.play(beep, loop=True)
160+
start_t = time.monotonic()
161+
while not button_right.value:
162+
pass
163+
react_t = time.monotonic()
164+
reaction_dur = react_t - start_t
165+
print(update_stats(statistics, "auditory", run, reaction_dur))
166+
audio.stop()
167+
audio.play(beep) # ensure speaker is left near midpoint
168+
169+
run += 1

0 commit comments

Comments
 (0)