/ 3D_Printed_LED_Fire_Horns / code.py
code.py
1 # SPDX-FileCopyrightText: 2018 Phil Burgess and Mikey Sklar for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 5 # Fiery demon horns (rawr!) for Adafruit Trinket/Gemma. 6 # Adafruit invests time and resources providing this open source code, 7 # please support Adafruit and open-source hardware by purchasing 8 # products from Adafruit! 9 10 import board 11 import neopixel 12 from analogio import AnalogIn 13 # pylint: disable=global-statement 14 15 try: 16 import urandom as random 17 except ImportError: 18 import random 19 20 # /\ -> Fire-like effect is the sum_total of multiple triangle 21 # ____/ \____ waves in motion, with a 'warm' color map applied. 22 n_horns = 1 # number of horns 23 led_pin = board.D0 # which pin your pixels are connected to 24 n_leds = 30 # number of LEDs per horn 25 frames_per_second = 50 # animation frames per second 26 brightness = 0 # current wave height 27 fade = 0 # Decreases brightness as wave moves 28 pixels = neopixel.NeoPixel(led_pin, n_leds, brightness=1, auto_write=False) 29 offset = 0 30 31 # Coordinate space for waves is 16x the pixel spacing, 32 # allowing fixed-point math to be used instead of floats. 33 lower = 0 # lower bound of wave 34 upper = 1 # upper bound of wave 35 mid = 2 # midpoint (peak) ((lower+upper)/2) 36 vlower = 3 # velocity of lower bound 37 vupper = 4 # velocity of upper bound 38 intensity = 5 # brightness at peak 39 40 y = 0 41 brightness = 0 42 count = 0 43 44 # initialize 3D list 45 wave = [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6 46 47 # Number of simultaneous waves (per horn) 48 n_waves = len(wave) 49 50 # Gamma-correction table 51 gamma = [ 52 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 54 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 55 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 56 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 57 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 58 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, 59 25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, 60 37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50, 61 51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68, 62 69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89, 63 90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 64 112, 114, 115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 65 135, 137, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 66 160, 162, 164, 167, 169, 171, 173, 175, 177, 180, 182, 184, 186, 67 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213, 215, 218, 68 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 69 255 70 ] 71 72 73 def random_wave(he, wi): 74 wave[he][wi][upper] = -1 # Always start below head of strip 75 wave[he][wi][lower] = -16 * (3 + random.randint(0,4)) # Lower end starts ~3-7 pixels back 76 wave[he][wi][mid] = (wave[he][wi][lower]+ wave[he][wi][upper]) / 2 77 wave[he][wi][vlower] = 3 + random.randint(0,4) # Lower end moves at ~1/8 to 1/pixels 78 wave[he][wi][vupper] = wave[he][wi][vlower]+ random.randint(0,4) # Upper end moves a bit faster 79 wave[he][wi][intensity] = 300 + random.randint(0,600) 80 81 def setup(): 82 global fade 83 84 # Random number generator is seeded from an unused 'floating' 85 # analog input - this helps ensure the random color choices 86 # aren't always the same order. 87 pin = AnalogIn(board.A0) 88 random.seed(pin.value) 89 pin.deinit() 90 91 for he in range(n_horns): 92 for wi in range(n_waves): 93 random_wave(he, wi) 94 95 fade = 233 + n_leds / 2 96 97 if fade > 233: 98 fade = 233 99 100 setup() 101 102 while True: 103 104 h = w = i = r = g = b = 0 105 x = 0 106 107 for h in range(n_horns): # For each horn... 108 x = 7 109 sum_total = 0 110 for i in range(n_leds): # For each LED along horn... 111 x += 16 112 for w in range(n_waves): # For each wave of horn... 113 if (x < wave[h][w][lower]) or (x > wave[h][w][upper]): 114 continue # Out of range 115 if x <= wave[h][w][mid]: # Lower half of wave (ramping up peak brightness) 116 sum_top = wave[h][w][intensity] * (x - wave[h][w][lower]) 117 sum_bottom = (wave[h][w][mid] - wave[h][w][lower]) 118 sum_total += sum_top / sum_bottom 119 else: # Upper half of wave (ramping down from peak) 120 sum_top = wave[h][w][intensity] * (wave[h][w][upper] - x) 121 sum_bottom = (wave[h][w][upper] - wave[h][w][mid]) 122 sum_total += sum_top / sum_bottom 123 124 sum_total = int(sum_total) # convert from decimal to whole number 125 126 # Now the magnitude (sum_total) is remapped to color for the LEDs. 127 # A blackbody palette is used - fades white-yellow-red-black. 128 if sum_total < 255: # 0-254 = black to red-1 129 r = gamma[sum_total] 130 g = b = 0 131 elif sum_total < 510: # 255-509 = red to yellow-1 132 r = 255 133 g = gamma[sum_total - 255] 134 b = 0 135 elif sum_total < 765: # 510-764 = yellow to white-1 136 r = g = 255 137 b = gamma[sum_total - 510] 138 else: # 765+ = white 139 r = g = b = 255 140 pixels[i] = (r, g, b) 141 142 for w in range(n_waves): # Update wave positions for each horn 143 wave[h][w][lower] += wave[h][w][vlower] # Advance lower position 144 if wave[h][w][lower] >= (n_leds * 16): # Off end of strip? 145 random_wave(h, w) # Yes, 'reboot' wave 146 else: # No, adjust other values... 147 wave[h][w][upper] += wave[h][w][vupper] 148 wave[h][w][mid] = (wave[h][w][lower] + wave[h][w][upper]) / 2 149 wave[h][w][intensity] = (wave[h][w][intensity] * fade) / 256 # Dimmer 150 151 pixels.show()