Skip to content

Commit 707edbf

Browse files
committed
2 parents 5dd0b2d + 195285d commit 707edbf

File tree

8 files changed

+13956
-0
lines changed

8 files changed

+13956
-0
lines changed
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
"""
2+
CLUE BLE MIDI
3+
Sends MIDI CC values based on accelerometer x & y and proximity sensor
4+
Touch #0 switches Bank/Preset patches
5+
Touch #1 picks among the three CC lines w A&B buttons adjusting CC numbers
6+
Touch #2 starts/stops sending CC messages (still allows Program Change)
7+
"""
8+
import time
9+
from adafruit_clue import clue
10+
import adafruit_ble
11+
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
12+
import adafruit_ble_midi
13+
import adafruit_midi
14+
from adafruit_midi.control_change import ControlChange
15+
from adafruit_midi.program_change import ProgramChange
16+
17+
# from adafruit_midi.note_on import NoteOn
18+
# from adafruit_midi.pitch_bend import PitchBend
19+
import simpleio
20+
import displayio
21+
import terminalio
22+
from adafruit_display_text import label
23+
from adafruit_display_shapes.rect import Rect
24+
25+
# --- Pick your midi out channel here ---
26+
midi_channel = 1
27+
# --- Pick your MIDI CC numbers here ---
28+
cc_x_num = 71
29+
cc_y_num = 72
30+
cc_prox_num = 73
31+
# --- Pick Bank & Preset pairs here ---
32+
touch_patch = [ # first number is the Bank, second is the Preset
33+
(4, 16), # minimoog: Leads > Original MINI
34+
(5, 8), # Pads > Intergalactic Pass
35+
(0, 13), # Bass > Kraft Bass
36+
(6, 9), # Percussion > Space Hat
37+
]
38+
39+
patch_count = len(touch_patch)
40+
patch_index = (
41+
patch_count - 1
42+
) # start on the last one so first time it is pressed it goes to first
43+
44+
cc_x = 0
45+
cc_y = 0
46+
cc_prox = 0
47+
48+
# Use default HID descriptor
49+
midi_service = adafruit_ble_midi.MIDIService()
50+
advertisement = ProvideServicesAdvertisement(midi_service)
51+
52+
ble = adafruit_ble.BLERadio()
53+
if ble.connected:
54+
for c in ble.connections:
55+
c.disconnect()
56+
57+
midi = adafruit_midi.MIDI(midi_out=midi_service, out_channel=midi_channel - 1)
58+
59+
print("advertising")
60+
ble.name = "CLUE BLE MIDI"
61+
ble.start_advertising(advertisement)
62+
63+
clue.display.brightness = 1.0
64+
clue.pixel.brightness = 0.2
65+
screen = displayio.Group(max_size=17)
66+
67+
ORANGE = 0xCE6136
68+
GRAY = 0x080808
69+
BLACK = 0x121212
70+
BLUE = 0x668190
71+
SILVER = 0xAAAAAA
72+
BROWN = 0x805D40
73+
74+
# --- Setup screen ---
75+
# BG
76+
color_bitmap = displayio.Bitmap(240, 240, 1)
77+
color_palette = displayio.Palette(1)
78+
color_palette[0] = GRAY
79+
bg_sprite = displayio.TileGrid(color_bitmap, x=0, y=0, pixel_shader=color_palette)
80+
screen.append(bg_sprite)
81+
column_a = 20
82+
column_b = 168
83+
# positions that are distributed relative to cc_x and cc_prox y positions
84+
row_a = 80
85+
row_c = 170
86+
row_b = int(row_a + ((row_c - row_a) / 2))
87+
line_row_a = int(row_a + ((row_b - row_a) / 2))
88+
line_row_b = int(row_b + ((row_c - row_b) / 2))
89+
picker_box_row = [row_a, row_b, row_c]
90+
91+
# trim
92+
top_trim_box = Rect(0, 0, 240, 8, fill=BROWN, outline=None)
93+
screen.append(top_trim_box)
94+
bottom_trim_box = Rect(0, 232, 240, 8, fill=BROWN, outline=None)
95+
screen.append(bottom_trim_box)
96+
97+
# title text
98+
title_label = label.Label(
99+
terminalio.FONT, text="MIDI CLUE", scale=4, color=SILVER, max_glyphs=14
100+
)
101+
title_label.x = 14
102+
title_label.y = 27
103+
screen.append(title_label)
104+
105+
# title box
106+
title_box = Rect(0, 54, 240, 8, fill=BROWN, outline=None)
107+
screen.append(title_box)
108+
109+
# cc x num
110+
cc_x_num_label = label.Label(
111+
terminalio.FONT,
112+
text=("CC {}".format(cc_x_num)),
113+
scale=3,
114+
color=ORANGE,
115+
max_glyphs=6,
116+
)
117+
cc_x_num_label.x = column_a
118+
cc_x_num_label.y = row_a
119+
screen.append(cc_x_num_label)
120+
121+
# cc x value
122+
cc_x_label = label.Label(
123+
terminalio.FONT, text=(cc_x), scale=3, color=ORANGE, max_glyphs=3
124+
)
125+
cc_x_label.x = column_b
126+
cc_x_label.y = row_a
127+
screen.append(cc_x_label)
128+
129+
# picker box
130+
picker_box = Rect(3, row_a, 6, 6, fill=ORANGE, outline=None)
131+
screen.append(picker_box)
132+
133+
# mid line
134+
mid_line_a = Rect(0, line_row_a, 240, 2, fill=SILVER, outline=None)
135+
screen.append(mid_line_a)
136+
137+
# cc y num
138+
cc_y_num_label = label.Label(
139+
terminalio.FONT, text=("CC {}".format(cc_y_num)), scale=3, color=BLUE, max_glyphs=6
140+
)
141+
cc_y_num_label.x = column_a
142+
cc_y_num_label.y = row_b
143+
screen.append(cc_y_num_label)
144+
145+
# cc y value text
146+
cc_y_label = label.Label(terminalio.FONT, text=cc_y, scale=3, color=BLUE, max_glyphs=3,)
147+
cc_y_label.x = column_b
148+
cc_y_label.y = row_b
149+
screen.append(cc_y_label)
150+
151+
# mid line
152+
mid_line_b = Rect(0, line_row_b, 240, 2, fill=SILVER, outline=None)
153+
screen.append(mid_line_b)
154+
155+
# cc prox num text
156+
cc_prox_num_label = label.Label(
157+
terminalio.FONT,
158+
text=("CC {}".format(cc_prox_num)),
159+
scale=3,
160+
color=SILVER,
161+
max_glyphs=6,
162+
)
163+
cc_prox_num_label.x = column_a
164+
cc_prox_num_label.y = row_c
165+
screen.append(cc_prox_num_label)
166+
167+
# cc prox value text
168+
cc_prox_label = label.Label(
169+
terminalio.FONT, text=cc_prox, scale=3, color=SILVER, max_glyphs=3,
170+
)
171+
cc_prox_label.x = column_b
172+
cc_prox_label.y = row_c
173+
screen.append(cc_prox_label)
174+
175+
# footer line
176+
footer_line = Rect(0, 192, 240, 2, fill=SILVER, outline=None)
177+
screen.append(footer_line)
178+
179+
180+
# patch label
181+
patch_label = label.Label(
182+
terminalio.FONT, text=("Patch _"), scale=2, color=BLUE, max_glyphs=12,
183+
)
184+
patch_label.x = 4
185+
patch_label.y = 216
186+
screen.append(patch_label)
187+
188+
# footer label
189+
footer_label = label.Label(
190+
terminalio.FONT, text=("connect BLE"), scale=2, color=ORANGE, max_glyphs=14
191+
)
192+
footer_label.x = 102
193+
footer_label.y = 216
194+
screen.append(footer_label)
195+
196+
# show the screen
197+
clue.display.show(screen)
198+
199+
cc_num_pick_toggle = 0 # which cc to adjust w buttons
200+
cc_send_toggle = True # to start and stop sending cc
201+
202+
debug = False # set debug mode True to test raw values, set False to run BLE MIDI
203+
204+
while True:
205+
if debug:
206+
accel_data = clue.acceleration # get accelerometer reading
207+
accel_x = accel_data[0]
208+
accel_y = accel_data[1]
209+
prox_data = clue.proximity
210+
print("x:{} y:{}".format(accel_x, accel_y,))
211+
print("proximity: {}".format(clue.proximity))
212+
time.sleep(0.2)
213+
214+
else:
215+
print("Waiting for connection")
216+
while not ble.connected:
217+
pass
218+
print("Connected")
219+
footer_label.x = 80
220+
footer_label.color = BLUE
221+
footer_label.text = "BLE Connected"
222+
time.sleep(2)
223+
footer_label.x = 110
224+
footer_label.color = SILVER
225+
footer_label.text = "sending CC"
226+
227+
while ble.connected:
228+
# Clue sensor readings to CC
229+
accel_data = clue.acceleration # get accelerometer reading
230+
accel_x = accel_data[0]
231+
accel_y = accel_data[1]
232+
prox_data = clue.proximity
233+
234+
# Remap analog readings to cc range
235+
cc_x = int(simpleio.map_range(accel_x, -9, 9, 0, 127))
236+
cc_y = int(simpleio.map_range(accel_y, 0, 9, 0, 127))
237+
cc_prox = int(simpleio.map_range(prox_data, 0, 255, 0, 127))
238+
239+
# send all the midi messages in a list
240+
if cc_send_toggle:
241+
midi.send(
242+
[
243+
ControlChange(cc_x_num, cc_x),
244+
ControlChange(cc_y_num, cc_y),
245+
ControlChange(cc_prox_num, cc_prox),
246+
]
247+
)
248+
cc_x_label.text = cc_x
249+
cc_y_label.text = cc_y
250+
cc_prox_label.text = cc_prox
251+
252+
# If you want to send NoteOn or Pitch Bend, here are examples:
253+
# midi.send(NoteOn(44, 1column_a)) # G sharp 2nd octave
254+
# a_pitch_bend = PitchBend(random.randint(0, 16383))
255+
# midi.send(a_pitch_bend)
256+
257+
if clue.button_a:
258+
if cc_num_pick_toggle == 0:
259+
cc_x_num = cc_x_num - 1
260+
cc_x_num_label.text = "CC {}".format(cc_x_num)
261+
time.sleep(0.05) # Debounce
262+
elif cc_num_pick_toggle == 1:
263+
cc_y_num = cc_y_num - 1
264+
cc_y_num_label.text = "CC {}".format(cc_y_num)
265+
time.sleep(0.05)
266+
else:
267+
cc_prox_num = cc_prox_num - 1
268+
cc_prox_num_label.text = "CC {}".format(cc_prox_num)
269+
time.sleep(0.05)
270+
271+
if clue.button_b:
272+
if cc_num_pick_toggle == 0:
273+
cc_x_num = cc_x_num + 1
274+
cc_x_num_label.text = "CC {}".format(cc_x_num)
275+
time.sleep(0.05)
276+
elif cc_num_pick_toggle == 1:
277+
cc_y_num = cc_y_num + 1
278+
cc_y_num_label.text = "CC {}".format(cc_y_num)
279+
time.sleep(0.05)
280+
else:
281+
cc_prox_num = cc_prox_num + 1
282+
cc_prox_num_label.text = "CC {}".format(cc_prox_num)
283+
time.sleep(0.05)
284+
285+
if clue.touch_0:
286+
patch_index = (patch_index + 1) % patch_count
287+
midi.send( # Bank select
288+
[
289+
ControlChange(0, 0), # MSB
290+
ControlChange(32, touch_patch[patch_index][0]), # LSB
291+
]
292+
)
293+
midi.send(ProgramChange(touch_patch[patch_index][1])) # Program Change
294+
patch_label.text = "Patch {}".format(patch_index + 1)
295+
time.sleep(0.2)
296+
297+
if clue.touch_1:
298+
cc_num_pick_toggle = (cc_num_pick_toggle + 1) % 3
299+
picker_box.y = picker_box_row[cc_num_pick_toggle]
300+
time.sleep(0.1)
301+
302+
if clue.touch_2:
303+
cc_send_toggle = not cc_send_toggle
304+
if cc_send_toggle:
305+
footer_label.x = 110
306+
footer_label.color = SILVER
307+
footer_label.text = "sending CC"
308+
else:
309+
footer_label.x = 114
310+
footer_label.color = ORANGE
311+
footer_label.text = "CC paused"
312+
time.sleep(0.1)
313+
314+
print("Disconnected")
315+
print()
316+
ble.start_advertising(advertisement)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import sys
2+
import time
3+
import board
4+
from adafruit_pyportal import PyPortal
5+
cwd = ("/"+__file__).rsplit('/', 1)[0] # the current working directory (where this file is)
6+
sys.path.append(cwd)
7+
import electioncal_graphics # pylint: disable=wrong-import-position
8+
9+
# Get wifi details and more from a secrets.py file
10+
try:
11+
from secrets import secrets
12+
except ImportError:
13+
print("WiFi secrets are kept in secrets.py, please add them there!")
14+
raise
15+
16+
# Change this to your state and county, replacing spaces for underscores and in lowercase
17+
STATE="new_york"
18+
COUNTY="new_york"
19+
20+
DATA_SOURCE = "https://electioncal.us/en/" + STATE +"/" + COUNTY + "/voter.json"
21+
DATA_LOCATION = []
22+
23+
# Initialize the pyportal object and let us know what data to fetch and where
24+
# to display it
25+
pyportal = PyPortal(url=DATA_SOURCE,
26+
json_path=DATA_LOCATION,
27+
status_neopixel=board.NEOPIXEL,
28+
default_bg=0x000000)
29+
30+
31+
gfx = electioncal_graphics.Electioncal_Graphics(pyportal.splash, am_pm=True)
32+
display_refresh = None
33+
while True:
34+
# only query the online time once per hour (and on first run)
35+
if (not display_refresh) or (time.monotonic() - display_refresh) > 3600:
36+
try:
37+
print("Getting time from internet!")
38+
pyportal.get_local_time()
39+
display_refresh = time.monotonic()
40+
except RuntimeError as e:
41+
print("Some error occured, retrying! -", e)
42+
continue
43+
44+
try:
45+
value = pyportal.fetch()
46+
#print("Response is", value)
47+
gfx.load_data(value)
48+
except RuntimeError as e:
49+
print("Some error occured, retrying! -", e)
50+
continue
51+
try:
52+
gfx.elections_cycle()
53+
except RuntimeError as e:
54+
print("Some error ocurred, retrying! -", e)
55+
continue

0 commit comments

Comments
 (0)