288 lines
10 KiB
Python
288 lines
10 KiB
Python
# SPDX-FileCopyrightText: 2021 Erin St Blaine for Adafruit Industries
|
|
# SPDX-FileCopyrightText: 2021 Limor Fried for Adafruit Industries
|
|
#
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
"""
|
|
LED Snowboard with Feather M4 and PropMaker Wing
|
|
Adafruit invests time and resources providing this open source code.
|
|
Please support Adafruit and open source hardware by purchasing
|
|
products from Adafruit!
|
|
Written by Erin St Blaine & Limor Fried for Adafruit Industries
|
|
Copyright (c) 2020-2021 Adafruit Industries
|
|
Licensed under the MIT license.
|
|
All text above must be included in any redistribution.
|
|
|
|
|
|
|
|
"""
|
|
# pylint: disable=import-error
|
|
# pylint: disable=no-member
|
|
import time
|
|
import board
|
|
import digitalio
|
|
from digitalio import DigitalInOut, Direction, Pull
|
|
from rainbowio import colorwheel
|
|
import adafruit_lis3dh
|
|
import neopixel
|
|
from adafruit_led_animation.helper import PixelMap
|
|
from adafruit_led_animation.sequence import AnimationSequence
|
|
from adafruit_led_animation.group import AnimationGroup
|
|
from adafruit_led_animation.animation.sparkle import Sparkle
|
|
from adafruit_led_animation.animation.rainbow import Rainbow
|
|
from adafruit_led_animation.animation.rainbowchase import RainbowChase
|
|
from adafruit_led_animation.animation.rainbowcomet import RainbowComet
|
|
from adafruit_led_animation.animation.solid import Solid
|
|
from adafruit_led_animation.animation.chase import Chase
|
|
from adafruit_led_animation.animation.comet import Comet
|
|
from adafruit_led_animation.color import (
|
|
BLACK,
|
|
RED,
|
|
GREEN,
|
|
ORANGE,
|
|
BLUE,
|
|
PURPLE,
|
|
WHITE,
|
|
)
|
|
|
|
btn = DigitalInOut(board.A1)
|
|
btn.direction = Direction.INPUT
|
|
btn.pull = Pull.UP
|
|
|
|
prev_state = btn.value
|
|
|
|
THRESHOLD = -1 #shake threshold
|
|
CARVE_THRESHOLD = 5
|
|
|
|
# Set to the length in seconds for the animations
|
|
POWER_ON_DURATION = 2
|
|
|
|
NUM_PIXELS = 211 # Number of pixels used in project
|
|
NEOPIXEL_PIN = board.D5
|
|
POWER_PIN = board.D10
|
|
|
|
enable = digitalio.DigitalInOut(POWER_PIN)
|
|
enable.direction = digitalio.Direction.OUTPUT
|
|
enable.value = False
|
|
|
|
|
|
# Set up accelerometer on I2C bus, 4G range:
|
|
i2c = board.I2C() # uses board.SCL and board.SDA
|
|
# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller
|
|
accel = adafruit_lis3dh.LIS3DH_I2C(i2c)
|
|
accel.range = adafruit_lis3dh.RANGE_4_G
|
|
accel.set_tap(2, 15)
|
|
|
|
pixels = neopixel.NeoPixel(NEOPIXEL_PIN, NUM_PIXELS, brightness=1, auto_write=False)
|
|
pixels.fill(0) # NeoPixels off ASAP on startup
|
|
pixels.show()
|
|
|
|
|
|
#PIXEL MAPS: Used for reordering pixels so the animations can run in different configurations.
|
|
#My LED strips inside the neck are accidentally swapped left-right,
|
|
#so these maps also correct for that
|
|
|
|
|
|
#Bottom up along both sides at once
|
|
pixel_map_right = PixelMap(pixels, [
|
|
150, 151, 152, 149, 148, 147, 146, 145, 144, 143, 142,
|
|
141, 140, 139, 138, 137, 136, 135, 134, 133, 132, 131,
|
|
130, 129, 128, 127, 126, 125, 124, 123, 122, 121, 120,
|
|
119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109,
|
|
108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97,
|
|
96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83,
|
|
82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69,
|
|
68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55,
|
|
54, 53, 52, 51, 50, 49, 48, 47, 46,
|
|
], individual_pixels=True)
|
|
|
|
#Starts at the bottom and goes around clockwise
|
|
pixel_map_left = PixelMap(pixels, [
|
|
153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
|
|
165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
|
|
177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
|
|
190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
|
|
203, 204, 205, 206, 207, 208, 209, 210, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
|
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
|
|
], individual_pixels=True)
|
|
|
|
#Radiates from the center outwards like a starburst
|
|
pixel_map_radiate = PixelMap(pixels, [
|
|
206, 98, 205, 99, 207, 97, 204, 100, 208, 96, 203, 101, 209,
|
|
95, 202, 102, 210, 94, 0, 93, 92, 201, 103, 1, 91, 200, 104,
|
|
2, 90, 199, 105, 3, 89, 198, 106, 4, 88, 197, 107, 5, 87, 196,
|
|
108, 6, 86, 195, 109, 7, 85, 194, 110, 8, 84, 193, 111, 9, 83,
|
|
192, 112, 10, 82, 191, 113, 11, 81, 190, 114, 12, 80, 189, 115,
|
|
13, 79, 188, 116, 14, 78, 187, 117, 15, 77, 186, 118, 16, 76, 185,
|
|
119, 17, 75, 184, 120, 18, 74, 183, 121, 19, 73, 182, 122, 20, 72,
|
|
181, 123, 21, 71, 180, 124, 22, 70, 179, 125, 23, 69, 178, 126, 24,
|
|
68, 177, 127, 25, 67, 176, 128, 26, 66, 175, 129, 27, 65, 174, 130,
|
|
28, 64, 173, 131, 29, 63, 172, 132, 30, 62, 171, 133, 31, 61, 170, 134, 32,
|
|
60, 169, 135, 33, 69, 168, 136, 34, 58, 167, 137, 35, 57, 166, 138, 36, 56,
|
|
165, 139, 37, 55, 164, 140, 38, 54, 163, 141, 39, 53, 162, 142, 40, 52, 161,
|
|
143, 41, 51, 160, 144, 42, 50, 159, 145, 43, 49, 158, 146, 44, 48, 157, 147,
|
|
45, 47, 156, 148, 46, 46, 155, 149, 47, 154, 149, 153, 150, 152, 151,
|
|
], individual_pixels=True)
|
|
|
|
#Top down along both sides at once
|
|
pixel_map_sweep = PixelMap(pixels, [
|
|
151, 152, 150, 153, 149, 154, 148, 155, 147, 156, 146, 157, 145, 158,
|
|
144, 159, 143, 160, 142, 161, 141, 162, 140, 163, 139, 164, 138, 165,
|
|
137, 166, 136, 167, 135, 168, 134, 169, 133, 170, 132, 171, 131, 172,
|
|
130, 173, 129, 174, 128, 175, 127, 176, 126, 177, 125, 178, 124, 179,
|
|
123, 180, 122, 181, 121, 182, 120, 183, 119, 184, 118, 185, 117, 186,
|
|
116, 187, 115, 188, 114, 189, 113, 190, 112, 191, 111, 192, 110, 193,
|
|
109, 194, 108, 195, 107, 196, 106, 197, 105, 198, 104, 199, 103, 200,
|
|
102, 201, 101, 202, 100, 203, 99, 204, 98, 205, 97, 206, 96, 207, 95,
|
|
208, 94, 209, 93, 210, 92, 91, 0, 90, 1, 89, 2, 88, 3, 87, 4, 86, 5, 85,
|
|
6, 84, 7, 83, 8, 82, 9, 81, 10, 80, 11, 79, 12, 78, 13, 77, 14, 76, 15,
|
|
75, 16, 74, 17, 73, 18, 72, 19, 71, 20, 70, 21, 69, 22, 68, 23, 67, 24,
|
|
66, 25, 65, 24, 64, 25, 63, 26, 62, 27, 61, 28, 60, 29, 59, 30, 58, 31,
|
|
57, 32, 56, 33, 55, 34, 54, 35, 53, 36, 52, 37, 51, 38, 50, 39, 49, 40,
|
|
48, 41, 47, 42, 46, 43, 45, 44,
|
|
], individual_pixels=True)
|
|
|
|
pixel_map_tail = PixelMap(pixels, [
|
|
15, 75, 16, 74, 17, 73, 18, 72, 19, 71, 20, 70, 21, 69, 22, 68, 23, 67,
|
|
24, 66, 25, 65, 24, 64, 25, 63, 26, 62, 27, 61, 28, 60, 29, 59, 30, 58,
|
|
31, 57, 32, 56, 33, 55, 34, 54, 35, 53, 36, 52, 37, 51, 38, 50, 39, 49,
|
|
40, 48, 41, 47, 42, 46, 43, 45, 44,
|
|
], individual_pixels=True)
|
|
|
|
pixel_map = [
|
|
pixel_map_right,
|
|
pixel_map_left,
|
|
pixel_map_radiate,
|
|
pixel_map_sweep,
|
|
pixel_map_tail,
|
|
]
|
|
|
|
|
|
def power_on(duration):
|
|
"""
|
|
Animate NeoPixels for power on.
|
|
"""
|
|
start_time = time.monotonic() # Save start time
|
|
while True:
|
|
elapsed = time.monotonic() - start_time # Time spent
|
|
if elapsed > duration: # Past duration?
|
|
break # Stop animating
|
|
powerup.animate()
|
|
|
|
|
|
# Cusomize LED Animations ------------------------------------------------------
|
|
powerup = RainbowComet(pixel_map[3], speed=0, tail_length=25, bounce=False)
|
|
rainbow = Rainbow(pixel_map[2], speed=0, period=6, name="rainbow", step=2.4)
|
|
rainbow_chase = RainbowChase(pixels, speed=0, size=6, spacing=15, step=10)
|
|
rainbow_chase2 = RainbowChase(pixels, speed=0, size=10, spacing=1, step=18, name="rainbow_chase2")
|
|
chase = RainbowChase(pixel_map[3], speed=0, size=20, spacing=20)
|
|
chase2 = Chase(pixels, speed=0.1, color=ORANGE, size=6, spacing=6)
|
|
rainbow_comet = RainbowComet(pixel_map[2], speed=0, tail_length=200, bounce=True)
|
|
rainbow_comet2 = RainbowComet(
|
|
pixels, speed=0, tail_length=104, colorwheel_offset=80, bounce=True
|
|
)
|
|
rainbow_comet3 = RainbowComet(
|
|
pixel_map[2], speed=0, tail_length=80, colorwheel_offset=80, step=4, bounce=False
|
|
)
|
|
strum = RainbowComet(
|
|
pixel_map[3], speed=0, tail_length=50, bounce=False, colorwheel_offset=50, step=4
|
|
)
|
|
fuego = RainbowComet(
|
|
pixel_map[4], speed=0.05, colorwheel_offset=40, step=2, tail_length=40
|
|
)
|
|
fuego2 = RainbowComet(
|
|
pixel_map[4], speed=0.02, colorwheel_offset=40, step=2, tail_length=40
|
|
)
|
|
lava = Comet(pixel_map[4], speed=0, color=ORANGE, tail_length=40, bounce=False)
|
|
sparkle = Sparkle(pixel_map[3], speed=0.05, color=BLUE, num_sparkles=10)
|
|
sparkle2 = Sparkle(pixels, speed=0.05, color=PURPLE, num_sparkles=4)
|
|
sparkle3 = Sparkle(pixels, speed=0, color=WHITE, num_sparkles=1)
|
|
carve_left = Solid(pixel_map[0], color=GREEN)
|
|
carve_right = Solid(pixel_map[1], color=RED)
|
|
black_left = Solid(pixel_map[0], color=BLACK, name="BLACK")
|
|
black_right = Solid(pixel_map[1], color=BLACK)
|
|
|
|
# Animations Playlist - reorder as desired. AnimationGroups play at the same time
|
|
animations = AnimationSequence(
|
|
AnimationGroup(
|
|
fuego,
|
|
fuego2,
|
|
lava,
|
|
sparkle,
|
|
),
|
|
chase,
|
|
rainbow_chase2,
|
|
rainbow,
|
|
AnimationGroup(
|
|
rainbow_comet,
|
|
sparkle3,
|
|
),
|
|
AnimationGroup(
|
|
rainbow_comet2,
|
|
sparkle3,
|
|
),
|
|
AnimationGroup(
|
|
sparkle,
|
|
strum,
|
|
),
|
|
AnimationGroup(
|
|
sparkle2,
|
|
rainbow_comet3,
|
|
),
|
|
AnimationGroup(
|
|
black_left,
|
|
black_right,
|
|
),
|
|
black_left,
|
|
auto_clear=True,
|
|
auto_reset=True,
|
|
)
|
|
|
|
|
|
MODE = 0
|
|
LASTMODE = 1
|
|
i = 0
|
|
|
|
# Main loop
|
|
while True:
|
|
i = (i + 0.5) % 256 # run from 0 to 255
|
|
TILT_COLOR = colorwheel(i)
|
|
if MODE == 0: # If currently off...
|
|
enable.value = True
|
|
power_on(POWER_ON_DURATION) # Power up!
|
|
MODE = LASTMODE
|
|
|
|
elif MODE >= 1: # If not OFF MODE...
|
|
# Read button
|
|
cur_state = btn.value
|
|
if cur_state != prev_state:
|
|
if not cur_state:
|
|
animations.next()
|
|
print("BTN is down")
|
|
else:
|
|
print("BTN is up")
|
|
prev_state = cur_state
|
|
# Read accelerometer
|
|
x, y, z = accel.acceleration
|
|
accel_total = z # x=tilt, y=rotate
|
|
accel_total_y = y # x=tilt, y=rotate
|
|
print(accel_total_y)
|
|
if accel_total > THRESHOLD:
|
|
print("THRESHOLD: ", accel_total)
|
|
if MODE == 1:
|
|
animations.animate()
|
|
if animations.current_animation.name == "BLACK":
|
|
MODE = 2
|
|
if MODE == 2:
|
|
if y > CARVE_THRESHOLD:
|
|
black_right.animate()
|
|
carve_left.animate()
|
|
if y < (CARVE_THRESHOLD * -1):
|
|
black_left.animate()
|
|
carve_right.animate()
|
|
#print (MODE)
|
|
cur_state = btn.value
|
|
if cur_state != prev_state:
|
|
if not cur_state:
|
|
MODE = 1
|