Skip to content

Commit 80dc085

Browse files
authored
Merge pull request #26 from FoamyGuy/ble_patchwork
adding ble patchwork
2 parents 97a3a63 + 9bdffd0 commit 80dc085

File tree

1 file changed

+281
-0
lines changed

1 file changed

+281
-0
lines changed

examples/clue_ble_color_patchwork.py

+281
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
"""
2+
Circuit Python BLE Color Patchwork
3+
This demo uses advertising to broadcast a color of the users choice.
4+
We will show a color "patch" on the screen for every unique device
5+
advertisement that we find.
6+
"""
7+
8+
import time
9+
import random
10+
import board
11+
from adafruit_clue import clue
12+
import displayio
13+
from adafruit_ble import BLERadio
14+
from adafruit_ble.advertising.adafruit import AdafruitColor
15+
from adafruit_display_shapes.rect import Rect
16+
from adafruit_display_text import label
17+
import terminalio
18+
19+
MODE_COLOR_SELECT = 0
20+
MODE_SHOW_PATCHWORK = 1
21+
22+
COLOR_TRANSPARENT_INDEX = 0
23+
COLOR_OFFWHITE_INDEX = 1
24+
25+
PROXIMITY_LIMIT = 100
26+
27+
current_mode = MODE_SHOW_PATCHWORK
28+
29+
# The color pickers will cycle through this list with buttons A and B.
30+
color_options = [
31+
0xEE0000,
32+
0xEEEE00,
33+
0x00EE00,
34+
0x00EEEE,
35+
0x0000EE,
36+
0xEE00EE,
37+
0xCCCCCC,
38+
0xFF9999,
39+
0x99FF99,
40+
0x9999FF,
41+
]
42+
43+
44+
# make all pixels in the patchwork bitmap transparent
45+
def make_transparent():
46+
palette_mapping.make_transparent(0)
47+
for i in range(0, 8):
48+
for j in range(0, 8):
49+
bitmap[i, j] = 0
50+
51+
52+
# make the pixels in the patchwork white by setting them in the palette
53+
def make_white():
54+
for i in range(2, 66):
55+
palette_mapping[i] = 0xFFFFFF
56+
57+
58+
# draw the patchwork grid based on nearby_colors
59+
def draw_grid():
60+
for i, color in enumerate(nearby_colors):
61+
if i < 64:
62+
palette_mapping[i + 2] = (
63+
color & 0xFFFFFF
64+
) # Mask 0xFFFFFF to avoid invalid color.
65+
print(i)
66+
print(color)
67+
68+
69+
# create a fake mac address and color for testing
70+
def add_fake():
71+
fake_mac = "".join([random.choice("0123456789abcdef") for _ in range(10)])
72+
fake_color = random.choice(color_options)
73+
nearby_addresses.append(fake_mac)
74+
nearby_colors.append(fake_color)
75+
76+
77+
# find nearby devices advertising colors
78+
def ble_scan():
79+
print("scanning")
80+
# loop over all found devices
81+
for entry in ble.start_scan(AdafruitColor, minimum_rssi=-100, timeout=1):
82+
# if this device is not in the list already
83+
if entry.color in color_options:
84+
print("new color")
85+
if entry.address.address_bytes not in nearby_addresses:
86+
# print(entry.color)
87+
# add the address and color to respective lists
88+
nearby_addresses.append(entry.address.address_bytes)
89+
nearby_colors.append(entry.color)
90+
else: # address was already in the list
91+
# update the color to currently advertised value
92+
_index = nearby_addresses.index(entry.address.address_bytes)
93+
nearby_colors[_index] = entry.color
94+
95+
96+
# set a new color to be advertised
97+
def change_advertisement(color):
98+
ble.stop_advertising()
99+
advertisement.color = color
100+
ble.start_advertising(advertisement)
101+
# set NeoPixel to selected advertised
102+
clue.pixel.fill(color)
103+
# update top left self patch
104+
nearby_colors[0] = color
105+
106+
107+
# BLE Setup
108+
ble = BLERadio()
109+
advertisement = AdafruitColor()
110+
advertisement.color = color_options[0]
111+
112+
# init neopixel
113+
clue.pixel.fill(color_options[0])
114+
clue.pixel.brightness = 0.05
115+
116+
display = board.DISPLAY
117+
118+
# Create a bitmap with two colors + 64 colors for the map
119+
bitmap = displayio.Bitmap(8, 8, 64 + 2)
120+
121+
# Create a 8*8 bitmap pre-filled with 64 colors (color 0 and 1 are reserved)
122+
for _i in range(0, 8):
123+
for _j in range(0, 8):
124+
bitmap[_i, _j] = 2 + _i + _j * 8
125+
126+
# Create an empty palette that will be used in one to one mapping
127+
palette_mapping = displayio.Palette(64 + 2)
128+
129+
palette_mapping[0] = 0x000000
130+
palette_mapping[1] = 0xFFFFFF
131+
132+
color_select_palette = displayio.Palette(len(color_options))
133+
for _i, _color in enumerate(color_options):
134+
color_select_palette[_i] = _color
135+
136+
# Color Select Layout
137+
color_select_group = displayio.Group(max_size=10)
138+
color_select_text_group = displayio.Group(max_size=4, scale=3)
139+
140+
# white backrground
141+
background = Rect(0, 0, 240, 240, fill=0xFFFFFF)
142+
center_line = Rect(119, 0, 2, 180, fill=0x000000)
143+
144+
# box around the color preview
145+
bottom_box = Rect(79, 174, 80, 80, fill=0xFFFFFF, outline=0x000000, stroke=2)
146+
147+
left_btn_lbl = label.Label(terminalio.FONT, text="A", color=0x000000)
148+
right_btn_lbl = label.Label(terminalio.FONT, text="B", color=0x000000)
149+
150+
left_btn_action = label.Label(
151+
terminalio.FONT, text="Next\nColor", color=0x000000, line_spacing=1
152+
)
153+
right_btn_action = label.Label(terminalio.FONT, text="Save", color=0x000000)
154+
155+
color_select_text_group.append(left_btn_lbl)
156+
color_select_text_group.append(right_btn_lbl)
157+
158+
color_select_text_group.append(left_btn_action)
159+
color_select_text_group.append(right_btn_action)
160+
161+
# x position centered on 25% of screen
162+
left_btn_lbl.anchor_point = (0.5, 0)
163+
left_btn_lbl.anchored_position = ((240 / 4) // 3, 21 // 3)
164+
165+
# x position centered on 75% of screen
166+
right_btn_lbl.anchor_point = (0.5, 0)
167+
right_btn_lbl.anchored_position = ((240 / 4), 21 // 3)
168+
169+
# x position centered on 25% of screen
170+
left_btn_action.anchor_point = (0.5, 0)
171+
left_btn_action.anchored_position = ((240 / 4) // 3, 96 // 3)
172+
173+
# x position centered on 75% of screen
174+
right_btn_action.anchor_point = (0.5, 0)
175+
right_btn_action.anchored_position = ((240 / 4), 96 // 3)
176+
177+
color_select_group.append(background)
178+
color_select_group.append(center_line)
179+
color_select_group.append(bottom_box)
180+
color_select_group.append(color_select_text_group)
181+
182+
# color preview bmp
183+
color_select_preview_bmp = displayio.Bitmap(1, 1, len(color_options))
184+
color_preview_group = displayio.Group(scale=30 * 2)
185+
186+
# centered horizontally near bottom on screen
187+
color_preview_group.x = 240 // 2 - 60 // 2
188+
color_preview_group.y = 240 - (60 + 2)
189+
190+
color_preview_tilegrid = displayio.TileGrid(
191+
color_select_preview_bmp, pixel_shader=color_select_palette
192+
)
193+
color_preview_group.append(color_preview_tilegrid)
194+
195+
# Create a TileGrid using the Bitmap and Palette
196+
tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette_mapping)
197+
patchwork_group = displayio.Group(scale=30)
198+
patchwork_group.append(tile_grid)
199+
200+
# Create main Group
201+
group = displayio.Group()
202+
203+
# Add the patchwork to the main
204+
group.append(patchwork_group)
205+
206+
# Add the Group to the Display
207+
display.show(group)
208+
209+
cur_color = 0
210+
211+
prev_b = clue.button_b
212+
prev_a = clue.button_a
213+
214+
nearby_addresses = ["myself"]
215+
nearby_colors = [color_options[cur_color]]
216+
217+
make_white()
218+
219+
last_scan_time = -30
220+
SCAN_INTERVAL = 30 # seconds
221+
222+
while True:
223+
now = time.monotonic()
224+
cur_a = clue.button_a
225+
cur_b = clue.button_b
226+
if current_mode == MODE_SHOW_PATCHWORK:
227+
# a button was pressed
228+
if cur_a and not prev_a:
229+
current_mode = MODE_COLOR_SELECT
230+
231+
# insert color select layout
232+
group.append(color_select_group)
233+
group.append(color_preview_group)
234+
235+
# is it time to scan?
236+
if last_scan_time + SCAN_INTERVAL < now:
237+
ble_scan()
238+
last_scan_time = now
239+
print("after scan found {} results".format(len(nearby_colors)))
240+
# print(nearby_addresses)
241+
draw_grid()
242+
243+
if clue.proximity >= PROXIMITY_LIMIT:
244+
clue.white_leds = True
245+
while clue.proximity >= PROXIMITY_LIMIT:
246+
r, g, b, w = clue.color
247+
clue.pixel.fill(((r >> 8) & 0xFF, (g >> 8) & 0xFF, (b >> 8) & 0xFF))
248+
change_advertisement(
249+
((r & 0xFF00) << 8) + (g & 0xFF00) + ((b >> 8) & 0xFF)
250+
)
251+
time.sleep(0.1)
252+
clue.white_leds = False
253+
254+
elif current_mode == MODE_COLOR_SELECT:
255+
# current selection preview
256+
color_select_preview_bmp[0, 0] = cur_color
257+
258+
# a button was pressed
259+
if cur_a and not prev_a:
260+
print("a button")
261+
# increment currently selected color index
262+
cur_color += 1
263+
# reset to 0 if it's too big
264+
if cur_color >= len(color_options):
265+
cur_color = 0
266+
print(cur_color)
267+
# b button was pressed
268+
if cur_b and not prev_b:
269+
print("b button")
270+
# advertise new color selection
271+
change_advertisement(color_options[cur_color])
272+
273+
# go back to patchwork mode
274+
current_mode = MODE_SHOW_PATCHWORK
275+
# remove color select background
276+
group.remove(color_select_group)
277+
group.remove(color_preview_group)
278+
make_white()
279+
draw_grid()
280+
prev_a = cur_a
281+
prev_b = cur_b

0 commit comments

Comments
 (0)