Skip to content

Commit f1310a5

Browse files
committed
adding ble patchwork
1 parent 89fec08 commit f1310a5

File tree

1 file changed

+273
-0
lines changed

1 file changed

+273
-0
lines changed

examples/clue_ble_color_patchwork.py

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

0 commit comments

Comments
 (0)