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()