Skip to content

Commit e531b32

Browse files
Add EyeLights accel input demo
1 parent cb920b2 commit e531b32

1 file changed

Lines changed: 119 additions & 0 deletions

File tree

  • EyeLights_Accelerometer_Rings
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""
2+
ACCELEROMETER INPUT DEMO: while the LED Glasses Driver has a perfectly
3+
good clicky button for input, this code shows how one might instead use
4+
the onboard accelerometer for interactions*.
5+
6+
Worn normally, the LED rings are simply lit a solid color.
7+
TAP the eyeglass frames to cycle among a list of available colors.
8+
LOOK DOWN to light the LED rings bright white -- for navigating steps
9+
or finding the right key. LOOK BACK UP to return to solid color.
10+
This uses only the rings, not the matrix portion.
11+
12+
* Like, if you have big ol' monster hands, that little button can be
13+
hard to click, y'know?
14+
"""
15+
16+
import time
17+
import board
18+
import digitalio
19+
import supervisor
20+
import adafruit_lis3dh
21+
import adafruit_is31fl3741
22+
from adafruit_is31fl3741.adafruit_ledglasses import LED_Glasses
23+
24+
i2c = board.I2C() # Shared by both the accelerometer and LED controller
25+
26+
# Initialize the accelerometer and enable single-tap detection
27+
int1 = digitalio.DigitalInOut(board.ACCELEROMETER_INTERRUPT)
28+
lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c, int1=int1)
29+
lis3dh.set_tap(1, 100)
30+
last_tap_time = 0
31+
32+
# Initialize the IS31 LED driver, buffered for smoother animation
33+
glasses = LED_Glasses(i2c, allocate=adafruit_is31fl3741.MUST_BUFFER)
34+
35+
# Here's a list of colors that we cycle through when tapped, specified
36+
# as (R,G,B) tuples from 0-255. These are intentionally a bit dim --
37+
# both to save battery and to make the "ground light" mode more dramatic.
38+
# Rather than primary color red/green/blue sequence which is just so
39+
# over-done at this point, let's use some HALLOWEEN colors!
40+
colors = ((27, 9, 0), (12, 0, 24), (5, 31, 0)) # Orange, purple, green
41+
color_index = 0 # Begin at first color in list
42+
43+
# Check accelerometer to see if we've started in the looking-down state,
44+
# set the target color (what we're aiming for) appropriately. Only the
45+
# Y axis is needed for this.
46+
_, filtered_y, _ = lis3dh.acceleration
47+
looking_down = filtered_y > 5
48+
target_color = (255, 255, 255) if looking_down else colors[color_index]
49+
50+
interpolated_color = (0, 0, 0) # LEDs off at startup, they'll ramp up
51+
52+
53+
def fill_color(color):
54+
"""Given an (R,G,B) tuple, fill both LED rings with this color."""
55+
# Convert tuple to a 'packed' 24-bit value
56+
packed = (int(color[0]) << 16) | (int(color[1]) << 8) | int(color[2])
57+
for i in range(24):
58+
glasses.left_ring[i] = glasses.right_ring[i] = packed
59+
60+
61+
while True: # Loop forever...
62+
63+
# The try/except here is because VERY INFREQUENTLY the I2C bus will
64+
# encounter an error when accessing either the accelerometer or the
65+
# LED driver, whether from bumping around the wires or sometimes an
66+
# I2C device just gets wedged. To more robustly handle the latter,
67+
# the code will restart if that happens.
68+
try:
69+
70+
# interpolated_color blends from the prior to the next ("target")
71+
# LED ring colors, with a pleasant ease-out effect.
72+
interpolated_color = (
73+
interpolated_color[0] * 0.85 + target_color[0] * 0.15,
74+
interpolated_color[1] * 0.85 + target_color[1] * 0.15,
75+
interpolated_color[2] * 0.85 + target_color[2] * 0.15,
76+
)
77+
# Fill both rings with interpolated_color, then refresh the LEDs.
78+
fill_color(interpolated_color)
79+
glasses.show()
80+
81+
# The look-down detection only needs the accelerometer's Y axis.
82+
# This works with the Glasses Driver mounted on either temple and
83+
# with the glasses arms "open" (as when worn).
84+
_, y, _ = lis3dh.acceleration
85+
# Smooth the accelerometer reading the same way RGB colors are
86+
# interpolated. This avoids false triggers from jostling around.
87+
filtered_y = filtered_y * 0.85 + y * 0.15
88+
# The threshold between "looking down" and "looking up" depends
89+
# on which of those states we're currently in. This is an example
90+
# of hysteresis in software...a change of direction requires a
91+
# little extra push before it takes, which avoids oscillating if
92+
# there was just a single threshold both ways.
93+
if looking_down: # Currently in the looking-down state
94+
_ = lis3dh.tapped # Discard any taps while looking down
95+
if filtered_y < 3.5: # Have we crossed the look-up threshold?
96+
target_color = colors[color_index]
97+
looking_down = False # We're looking up now!
98+
else: # Currently in looking-up state
99+
if filtered_y > 5: # Crossed the look-down threshold?
100+
target_color = (255, 255, 255)
101+
looking_down = True # We're looking down now!
102+
elif lis3dh.tapped:
103+
# No look up/down change, but the accelerometer registered
104+
# a tap. Compare this against the last time we sensed one,
105+
# and only do things if it's been more than half a second.
106+
# This avoids spurious double-taps that can occur no matter
107+
# how carefully the tap threshold was set.
108+
now = time.monotonic()
109+
elapsed = now - last_tap_time
110+
if elapsed > 0.5:
111+
# A good tap was detected. Cycle to the next color in
112+
# the list and note the time of this tap.
113+
color_index = (color_index + 1) % len(colors)
114+
target_color = colors[color_index]
115+
last_tap_time = now
116+
117+
# See "try" notes above regarding rare I2C errors.
118+
except OSError:
119+
supervisor.reload()

0 commit comments

Comments
 (0)