Skip to content

Commit 7ab0107

Browse files
Merge pull request #1845 from PaintYourDragon/main
Add EyeLights Googly Rings, rename Accelerometer Rings to disambiguate
2 parents c0207cf + 7eb1b6f commit 7ab0107

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

EyeLights_Googly_Rings/code.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"""
2+
GOOGLY EYES for Adafruit EyeLight LED glasses + driver. Pendulum physics
3+
simulation using accelerometer and math. This uses only the rings, not the
4+
matrix portion. Adapted from Bill Earl's STEAM-Punk Goggles project:
5+
https://learn.adafruit.com/steam-punk-goggles
6+
"""
7+
8+
import math
9+
import random
10+
import board
11+
import supervisor
12+
import adafruit_lis3dh
13+
import adafruit_is31fl3741
14+
from adafruit_is31fl3741.adafruit_ledglasses import LED_Glasses
15+
16+
17+
# HARDWARE SETUP ----
18+
19+
i2c = board.I2C() # Shared by both the accelerometer and LED controller
20+
21+
# Initialize the accelerometer
22+
lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c)
23+
24+
# Initialize the IS31 LED driver, buffered for smoother animation
25+
glasses = LED_Glasses(i2c, allocate=adafruit_is31fl3741.MUST_BUFFER)
26+
27+
28+
# PHYSICS SETUP -----
29+
30+
31+
class Pendulum:
32+
"""A small class for our pendulum simulation."""
33+
34+
def __init__(self, ring, color):
35+
"""Initial pendulum position, plus axle friction, are randomized
36+
so the two rings don't spin in perfect lockstep."""
37+
self.ring = ring # Save reference to corresponding LED ring
38+
self.color = color # (R,G,B) tuple for color
39+
self.angle = random.random() # Position around ring, in radians
40+
self.momentum = 0
41+
self.friction = random.uniform(0.85, 0.9) # Inverse friction, really
42+
43+
def interp(self, pixel, scale):
44+
"""Given a pixel index (0-23) and a scaling factor (0.0-1.0),
45+
interpolate between LED "off" color (at 0.0) and this item's fully-
46+
lit color (at 1.0) and set pixel to the result."""
47+
self.ring[pixel] = (
48+
(int(self.color[0] * scale) << 16)
49+
| (int(self.color[1] * scale) << 8)
50+
| int(self.color[2] * scale)
51+
)
52+
53+
def iterate(self, xyz):
54+
"""Given an accelerometer reading, run one cycle of the pendulum
55+
physics simulation and render the corresponding LED ring."""
56+
# Minus here is because LED pixel indices run clockwise vs. trigwise.
57+
# 0.05 is just an empirically-derived scaling fudge factor that looks
58+
# good; smaller values for more sluggish rings, higher = more twitch.
59+
self.momentum = (
60+
self.momentum * self.friction
61+
- (math.cos(self.angle) * xyz[2] + math.sin(self.angle) * xyz[0]) * 0.05
62+
)
63+
self.angle += self.momentum
64+
65+
# Scale pendulum angle into pixel space
66+
midpoint = self.angle * 12 / math.pi % 24
67+
# Go around the whole ring, setting each pixel based on proximity
68+
# (this is also to erase the prior position)...
69+
for i in range(24):
70+
dist = abs(midpoint - i) # Pixel to pendulum distance...
71+
if dist > 12: # If it crosses the "seam" at top,
72+
dist = 24 - dist # take the shorter path.
73+
if dist > 5: # Not close to pendulum,
74+
self.ring[i] = 0 # erase pixel.
75+
elif dist < 2: # Close to pendulum,
76+
self.interp(i, 1.0) # solid color
77+
else: # Anything in-between,
78+
self.interp(i, (5 - dist) / 3) # interpolate
79+
80+
81+
# List of pendulum objects, of which there are two: one per glasses ring
82+
pendulums = [
83+
Pendulum(glasses.left_ring, (0, 20, 50)), # Cerulean blue,
84+
Pendulum(glasses.right_ring, (0, 20, 50)), # 50 is plenty bright!
85+
]
86+
87+
88+
# MAIN LOOP ---------
89+
90+
while True:
91+
92+
# The try/except here is because VERY INFREQUENTLY the I2C bus will
93+
# encounter an error when accessing either the accelerometer or the
94+
# LED driver, whether from bumping around the wires or sometimes an
95+
# I2C device just gets wedged. To more robustly handle the latter,
96+
# the code will restart if that happens.
97+
try:
98+
99+
accel = lis3dh.acceleration
100+
for p in pendulums:
101+
p.iterate(accel)
102+
103+
glasses.show()
104+
105+
# See "try" notes above regarding rare I2C errors.
106+
except OSError:
107+
supervisor.reload()

0 commit comments

Comments
 (0)