Skip to content

Commit c9fa4d0

Browse files
authored
Merge branch 'master' into master
2 parents 0a72ca8 + ec0ab99 commit c9fa4d0

File tree

6 files changed

+471
-1
lines changed

6 files changed

+471
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ Hue_Controller/secrets.h
33
.idea
44
*.DS_Store
55
CircuitPython_Logger/secrets\.py
6+
.python-version
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""
2+
This example acts as a BLE HID keyboard to peer devices.
3+
Attach five buttons with pullup resistors to Feather nRF52840
4+
each button will send a configurable keycode to mobile device or computer
5+
"""
6+
import time
7+
import board
8+
from digitalio import DigitalInOut, Direction
9+
10+
import adafruit_ble
11+
from adafruit_ble.advertising import Advertisement
12+
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
13+
from adafruit_ble.services.standard.hid import HIDService
14+
from adafruit_ble.services.standard.device_info import DeviceInfoService
15+
from adafruit_hid.keyboard import Keyboard
16+
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
17+
from adafruit_hid.keycode import Keycode
18+
19+
button_1 = DigitalInOut(board.D11)
20+
button_2 = DigitalInOut(board.D10)
21+
button_3 = DigitalInOut(board.D9)
22+
button_4 = DigitalInOut(board.D6)
23+
button_5 = DigitalInOut(board.D5)
24+
25+
button_1.direction = Direction.INPUT
26+
button_2.direction = Direction.INPUT
27+
button_3.direction = Direction.INPUT
28+
button_4.direction = Direction.INPUT
29+
button_5.direction = Direction.INPUT
30+
31+
hid = HIDService()
32+
33+
device_info = DeviceInfoService(software_revision=adafruit_ble.__version__,
34+
manufacturer="Adafruit Industries")
35+
advertisement = ProvideServicesAdvertisement(hid)
36+
advertisement.appearance = 961
37+
scan_response = Advertisement()
38+
scan_response.complete_name = "CircuitPython HID"
39+
40+
ble = adafruit_ble.BLERadio()
41+
if not ble.connected:
42+
print("advertising")
43+
ble.start_advertising(advertisement, scan_response)
44+
else:
45+
print("already connected")
46+
print(ble.connections)
47+
48+
k = Keyboard(hid.devices)
49+
kl = KeyboardLayoutUS(k)
50+
while True:
51+
while not ble.connected:
52+
pass
53+
print("Start typing:")
54+
55+
while ble.connected:
56+
if not button_1.value: # pull up logic means button low when pressed
57+
#print("back") # for debug in REPL
58+
k.send(Keycode.BACKSPACE)
59+
time.sleep(0.1)
60+
61+
if not button_2.value:
62+
kl.write("Bluefruit") # use keyboard_layout for words
63+
time.sleep(0.4)
64+
65+
if not button_3.value:
66+
k.send(Keycode.SHIFT, Keycode.L) # add shift modifier
67+
time.sleep(0.4)
68+
69+
if not button_4.value:
70+
kl.write("e")
71+
time.sleep(0.4)
72+
73+
if not button_5.value:
74+
k.send(Keycode.ENTER)
75+
time.sleep(0.4)
76+
77+
ble.start_advertising(advertisement)

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

CPB_Volume_BLE/cpb_volume_ble.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
"""
2+
A CircuitPython 'multimedia' dial demo
3+
Uses a Circuit Playground Bluefruit + Rotary Encoder -> BLE out
4+
Knob controls volume, push encoder for mute, CPB button A for Play/Pause
5+
Once paired, bonding will auto re-connect devices
6+
"""
7+
8+
import time
9+
import digitalio
10+
import board
11+
import rotaryio
12+
import neopixel
13+
from adafruit_hid.consumer_control import ConsumerControl
14+
from adafruit_hid.consumer_control_code import ConsumerControlCode
15+
16+
import adafruit_ble
17+
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
18+
from adafruit_ble.services.standard.hid import HIDService
19+
from adafruit_ble.services.standard.device_info import DeviceInfoService
20+
21+
22+
ble = adafruit_ble.BLERadio()
23+
ble.name = "Bluefruit-Volume-Control"
24+
# Using default HID Descriptor.
25+
hid = HIDService()
26+
device_info = DeviceInfoService(software_revision=adafruit_ble.__version__,
27+
manufacturer="Adafruit Industries")
28+
advertisement = ProvideServicesAdvertisement(hid)
29+
cc = ConsumerControl(hid.devices)
30+
31+
FILL_COLOR = (0, 32, 32)
32+
UNMUTED_COLOR = (0, 128, 128)
33+
MUTED_COLOR = (128, 0, 0)
34+
DISCONNECTED_COLOR = (40, 40, 0)
35+
36+
# NeoPixel LED ring
37+
# Ring code will auto-adjust if not 16 so change to any value!
38+
ring = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=0.05, auto_write = False)
39+
ring.fill(DISCONNECTED_COLOR)
40+
ring.show()
41+
dot_location = 0 # what dot is currently lit
42+
43+
# CPB button for Play/Pause
44+
button_A = digitalio.DigitalInOut(board.BUTTON_A)
45+
button_A.switch_to_input(pull=digitalio.Pull.DOWN)
46+
47+
button_a_pressed = False # for debounce state
48+
49+
# Encoder button is a digital input with pullup on A1
50+
# so button.value == False means pressed.
51+
button = digitalio.DigitalInOut(board.A1)
52+
button.pull = digitalio.Pull.UP
53+
54+
encoder = rotaryio.IncrementalEncoder(board.A2, board.A3)
55+
56+
last_pos = encoder.position
57+
muted = False
58+
command = None
59+
# Disconnect if already connected, so that we pair properly.
60+
if ble.connected:
61+
for connection in ble.connections:
62+
connection.disconnect()
63+
64+
65+
def draw():
66+
if not muted:
67+
ring.fill(FILL_COLOR)
68+
ring[dot_location] = UNMUTED_COLOR
69+
else:
70+
ring.fill(MUTED_COLOR)
71+
ring.show()
72+
73+
74+
advertising = False
75+
connection_made = False
76+
print("let's go!")
77+
while True:
78+
if not ble.connected:
79+
ring.fill(DISCONNECTED_COLOR)
80+
ring.show()
81+
connection_made = False
82+
if not advertising:
83+
ble.start_advertising(advertisement)
84+
advertising = True
85+
continue
86+
else:
87+
if connection_made:
88+
pass
89+
else:
90+
ring.fill(FILL_COLOR)
91+
ring.show()
92+
connection_made = True
93+
94+
advertising = False
95+
96+
pos = encoder.position
97+
delta = pos - last_pos
98+
last_pos = pos
99+
direction = 0
100+
101+
if delta > 0:
102+
command = ConsumerControlCode.VOLUME_INCREMENT
103+
direction = -1
104+
elif delta < 0:
105+
command = ConsumerControlCode.VOLUME_DECREMENT
106+
direction = 1
107+
108+
if direction:
109+
muted = False
110+
for _ in range(abs(delta)):
111+
cc.send(command)
112+
# spin neopixel LED around in the correct direction!
113+
dot_location = (dot_location + direction) % len(ring)
114+
draw()
115+
116+
if not button.value:
117+
if not muted:
118+
print("Muting")
119+
cc.send(ConsumerControlCode.MUTE)
120+
muted = True
121+
else:
122+
print("Unmuting")
123+
cc.send(ConsumerControlCode.MUTE)
124+
muted = False
125+
draw()
126+
while not button.value: # debounce
127+
time.sleep(0.1)
128+
129+
if button_A.value and not button_a_pressed: # button is pushed
130+
cc.send(ConsumerControlCode.PLAY_PAUSE)
131+
print("Play/Pause")
132+
button_a_pressed = True # state for debouncing
133+
time.sleep(0.05)
134+
135+
if not button_A.value and button_a_pressed:
136+
button_a_pressed = False
137+
time.sleep(0.05)

0 commit comments

Comments
 (0)