Created
November 30, 2021 15:24
-
-
Save rpavlik/1e927800f22fb6c16038a76497cc3fc7 to your computer and use it in GitHub Desktop.
Adafruit EyeLights Digital Rain
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# SPDX-FileCopyrightText: 2021 Phil Burgess for Adafruit Industries | |
# SPDX-FileCopyrightText: 2021, Ryan Pavlik <[email protected]> | |
# | |
# SPDX-License-Identifier: MIT | |
import math | |
import random | |
import time | |
from supervisor import reload | |
import board | |
from busio import I2C | |
import adafruit_is31fl3741 | |
from adafruit_is31fl3741.adafruit_ledglasses import LED_Glasses | |
# HARDWARE SETUP ----------------------- | |
# Manually declare I2C (not board.I2C() directly) to access 1 MHz speed... | |
i2c = I2C(board.SCL, board.SDA, frequency=1000000) | |
# Initialize the IS31 LED driver, buffered for smoother animation | |
glasses = LED_Glasses(i2c, allocate=adafruit_is31fl3741.MUST_BUFFER) | |
glasses.show() # Clear any residue on startup | |
glasses.global_current = 10 # Just middlin' bright, please | |
def gamma_correct_intensity(v: float) -> float: | |
return math.pow(v, 2.2) | |
class DigitalRaindrop: | |
def __init__(self, grid, max_speed=3): | |
self._grid_width = grid.width | |
self._grid_height = grid.height | |
self.max_speed = max_speed | |
# self.color = 0x00FF00 | |
self.max_length = 6 | |
self.dead = False | |
self.reset() | |
def reset(self): | |
self.col = random.randint(0, self._grid_width - 1) | |
self.row = 0 | |
self.speed = random.uniform(1, self.max_speed) | |
def get_intensity(self, row: int) -> float: | |
if row > self.row + 1: | |
return 0 | |
# quick ramp up "ahead" of the end | |
if row > self.row: | |
linear = 1 - (row - self.row) | |
# elif row == self.row: | |
# linear = 1 | |
else: | |
linear = max(0, 1 - (self.row - row) / self.max_length) | |
return math.pow(linear, 2.2) | |
def update(self, dt, glasses: LED_Glasses): | |
if self.dead: | |
return | |
if self.row - self.max_length > self._grid_height: | |
self.dead = True | |
return | |
self.row += self.speed * dt | |
for y in range(0, glasses.height): | |
alpha = self.get_intensity(y) | |
# print(y, alpha) | |
glasses.pixel(self.col, int(y), int(alpha * 256) << 8) | |
# MAIN LOOP ---------------------------- | |
NUM_DROPS = 5 | |
rain = [DigitalRaindrop(glasses) for _ in range(NUM_DROPS)] | |
last_full = None | |
prev = time.monotonic() | |
while True: | |
now = time.monotonic() | |
dt = now - prev | |
prev = now | |
try: | |
for drop in rain: | |
drop.update(dt, glasses) | |
if len(rain) == NUM_DROPS: | |
last_full = now | |
rain = [drop for drop in rain if not drop.dead] | |
if len(rain) < NUM_DROPS: | |
if random.uniform(0, now - last_full) > 0.5: | |
rain.append(DigitalRaindrop(glasses)) | |
glasses.show() # Buffered mode MUST use show() to refresh matrix | |
except OSError: # See "try" notes above regarding rare I2C errors. | |
print("Restarting") | |
reload() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment