Skip to content

Commit 1ebbc3b

Browse files
authored
Merge pull request #2649 from jedgarpark/bricktunes
first commit bricktunes code
2 parents ecc5d4d + 3a650b6 commit 1ebbc3b

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed

Bricktunes_LEGO_Color_Synth/code.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# SPDX-FileCopyrightText: 2023 John Park for Adafruit
2+
# SPDX-License-Identifier: MIT
3+
4+
# Bricktunes LEGO Color Synth
5+
# Feather RP2040 Prop-Maker + AS7341 Color Sensor
6+
# Color comparison code and chime library by CGrover
7+
8+
import time
9+
import math
10+
import board
11+
import digitalio
12+
import audiobusio
13+
from adafruit_as7341 import AS7341, Gain
14+
import audiomixer
15+
from cedargrove_chime import Chime, Voice, Material, Striker
16+
17+
DEBUG = False # Useful for tuning reference color values by printing them
18+
TOLERANCE = 800 # The color matching tolerance index (0 to 8 * max_sensor_count)
19+
20+
sensor = AS7341(board.STEMMA_I2C())
21+
sensor.astep = 128 # (999) The integration time step size in 2.78 microsecond increments
22+
sensor.atime = 50 # The integration time step count.
23+
sensor.gain = Gain.GAIN_256X
24+
sensor.led_current = 4 # increments in units of 4
25+
sensor.led = True
26+
max_sensor_count = (sensor.astep + 1) * (sensor.atime + 1)
27+
28+
# ===================================================
29+
# color lists as 8-channel tuples (channels[0:8])
30+
brick_full_spectrum_values = [
31+
(94, 1310, 1736, 1075, 592, 437, 497, 383), # Blue
32+
(148, 324, 838, 2577, 2363, 1259, 929, 819), # Bright Green
33+
(381, 576, 850, 1619, 3688, 5532, 6291, 4250), # Bright Lt Orange
34+
(404, 2300, 2928, 2385, 2679, 3804, 5576, 4284), # Bright Pink
35+
(545, 1276, 1513, 1178, 2291, 6579, 6579, 6486), # Coral
36+
(136, 1055, 1223, 745, 748, 768, 1205, 1100), # Dark Purple
37+
(85, 731, 1375, 1604, 1019, 557, 533, 370), # Dark Turquoise
38+
(451, 2758, 3786, 2880, 3007, 3064, 4539, 3656), # Lavender
39+
(214, 300, 771, 1811, 3245, 2897, 2051, 1392), # Lime
40+
(188, 341, 435, 507, 625, 1703, 4361, 3692), # Red
41+
(182, 870, 1455, 1799, 2149, 1879, 1702, 1273), # Sand Green
42+
(461, 497, 878, 2412, 4699, 5935, 6579, 4677) # Yellow
43+
]
44+
45+
brick_color_names = [
46+
"Blue",
47+
"Bright Green",
48+
"Bright Light Orange",
49+
"Bright Pink",
50+
"Coral",
51+
"Dark Purple",
52+
"Dark Turquoise",
53+
"Lavender",
54+
"Lime",
55+
"Red",
56+
"Sand Green",
57+
"Yellow"
58+
]
59+
60+
brick_states = [False] * (len(brick_color_names))
61+
gap_state = False
62+
63+
# ===================================================
64+
# audio setup
65+
power = digitalio.DigitalInOut(board.EXTERNAL_POWER)
66+
power.switch_to_output(value=True)
67+
audio_output = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
68+
69+
mixer = audiomixer.Mixer(sample_rate=11020, buffer_size=4096, voice_count=1, channel_count=1)
70+
audio_output.play(mixer)
71+
mixer.voice[0].level = 0.50 # adjust this for overall volume
72+
73+
brickscale = [
74+
"C5", "D5", "E5", "F5", "G5", "A5", "B5",
75+
"C6", "D6", "E6", "F6", "G6", "A6", "B6",
76+
"C7", "D7", "E7", "F7", "G7", "A7", "B7",
77+
]
78+
79+
# Instantiate the chime synthesizer with custom parameters
80+
chime = Chime(
81+
mixer.voice[0],
82+
scale=brickscale,
83+
material=Material.Brass, # SteelEMT, Ceramic, Wood, Copper, Aluminum, Brass
84+
striker=Striker.HardWood, # Metal, Plexiglas, SoftWood, HardWood
85+
voice=Voice.Tubular, # bell, perfect, tubular
86+
scale_offset=-16
87+
)
88+
89+
# Play scale notes sequentially
90+
for index, note in enumerate(chime.scale):
91+
chime.strike(note, 1.0)
92+
time.sleep(0.1)
93+
time.sleep(1)
94+
95+
def compare_n_channel_colors(color_1, color_2, tolerance=0):
96+
"""Compares two integer multichannel count tuples using an unweighted linear
97+
Euclidean difference. If the color value difference is within the tolerance
98+
band of the reference, the method returns True.
99+
The difference value index `tolerance` is used to detect color similarity.
100+
Value range is an integer value from 0 to
101+
(maximum_channel_count * number_of_channels). Default is 0 (detects a
102+
single color value)."""
103+
# Create list of channel deltas using list comprehension
104+
deltas = [((color_1[idx] - count) ** 2) for idx, count in enumerate(color_2)]
105+
# Resolve squared deltas to a Euclidean difference
106+
# pylint: disable=c-extension-no-member
107+
delta_color = math.sqrt(sum(deltas))
108+
return bool(delta_color <= tolerance)
109+
110+
print("Bricktunes ready")
111+
112+
113+
while True:
114+
sensor_color = sensor.all_channels
115+
# don't bother to check comparison when we're looking at a gap between bricks
116+
if sensor_color[0] <= 70: # this checks for a minimum value on one channel
117+
if gap_state is False:
118+
print("no brick...")
119+
for i in range(len(brick_color_names)):
120+
brick_states[i] = False
121+
gap_state = True
122+
123+
else:
124+
if DEBUG:
125+
print(sensor_color)
126+
for i in range(len(brick_full_spectrum_values)):
127+
color_match = compare_n_channel_colors(
128+
sensor_color,
129+
brick_full_spectrum_values[i],
130+
TOLERANCE
131+
)
132+
133+
if color_match is True:
134+
if brick_states[i] is False:
135+
for n in range(5):
136+
chime.strike(chime.scale[i+(n*2)], 1.0)
137+
time.sleep(0.1)
138+
brick_states[i] = True
139+
gap_state = False
140+
print("sensor color:", sensor_color, "| ref:", brick_full_spectrum_values[i])
141+
print(brick_color_names[i])
142+
break

0 commit comments

Comments
 (0)