code.py
  1  # SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  # ATMakers HandUp
  6  # Listens to the USB Serial port and responds to incoming strings
  7  # Sets appropriate colors on the DotStar LED
  8  
  9  # This program uses the board package to access the Trinket's pin names
 10  # and uses adafruit_dotstar to talk to the LED
 11  # other boards would use the neopixel library instead
 12  
 13  from time import sleep
 14  import board
 15  from rainbowio import colorwheel
 16  import adafruit_dotstar
 17  import supervisor
 18  
 19  # create an object for the dotstar pixel on the Trinket M0
 20  # It's an array because it's a sequence of one pixel
 21  pixels = adafruit_dotstar.DotStar(board.APA102_SCK, board.APA102_MOSI, 1, brightness=.95)
 22  
 23  # this function takes a standard "hex code" for a color and returns
 24  # a tuple of (red, green, blue)
 25  def hex2rgb(hex_code):
 26      red = int("0x"+hex_code[0:2], 16)
 27      green = int("0x"+hex_code[2:4], 16)
 28      blue = int("0x"+hex_code[4:6], 16)
 29      rgb = (red, green, blue)
 30      # print(rgb)
 31      return rgb
 32  
 33  # This array contains digitized data for a heartbeat wave scaled to between 0 and 1.0
 34  # It is used to create the "beat" mode. Ensure each line is <78 characters for Travis-CI
 35  beatArray = [0.090909091,0.097902098,0.104895105,0.118881119,0.132867133,0.146853147,
 36               0.153846154,0.160839161,0.181818182,0.181818182,0.195804196,0.181818182,0.188811189,
 37               0.188811189,0.181818182,0.174825175,0.174825175,0.160839161,0.167832168,0.160839161,
 38               0.167832168,0.167832168,0.167832168,0.160839161,0.146853147,0.146853147,0.153846154,
 39               0.160839161,0.146853147,0.153846154,0.13986014,0.153846154,0.132867133,0.146853147,
 40               0.13986014,0.13986014,0.146853147,0.146853147,0.146853147,0.146853147,0.160839161,
 41               0.146853147,0.160839161,0.167832168,0.181818182,0.202797203,0.216783217,0.20979021,
 42               0.202797203,0.195804196,0.195804196,0.216783217,0.160839161,0.13986014,0.13986014,
 43               0.13986014,0.118881119,0.118881119,0.111888112,0.132867133,0.111888112,0.132867133,
 44               0.104895105,0.083916084,0.020979021,0,0.230769231,0.636363636,1,0.846153846,
 45               0.27972028,0.048951049,0.055944056,0.083916084,0.090909091,0.083916084,0.083916084,
 46               0.076923077,0.076923077,0.076923077,0.090909091,0.06993007,0.083916084,0.076923077,
 47               0.076923077,0.06993007,0.076923077,0.083916084,0.083916084,0.083916084,0.076923077,
 48               0.090909091,0.076923077,0.083916084,0.06993007,0.076923077,0.062937063,0.06993007,
 49               0.062937063,0.055944056,0.055944056,0.048951049,0.041958042,0.034965035,0.041958042,
 50               0.027972028]
 51  
 52  # When we start up, make the LED black
 53  black = (0, 0, 0)
 54  # the color that's passed in over the text input
 55  targetColor = black
 56  
 57  # pos is used for all modes that cycle or progress
 58  # it loops from 0-255 and starts over
 59  pos = 0
 60  
 61  # curColor is the color that will be displayed at the end of the main loop
 62  # it is mapped using pos according to the mode
 63  curColor = black
 64  
 65  # the mode can be one of
 66  # solid - just keep the current color
 67  # blink - alternate between black and curColor
 68  # ramp  - transition continuously between black and curColor
 69  # beat - pulse to a recorded heartbeat intensity
 70  # wheel - change hue around the colorwheel (curColor is ignored)
 71  
 72  mode='wheel'
 73  
 74  # We start by turning off pixels
 75  pixels.fill(black)
 76  pixels.show()
 77  
 78  # Main Loop
 79  while True:
 80      # Check to see if there's input available (requires CP 4.0 Alpha)
 81      if supervisor.runtime.serial_bytes_available:
 82          # read in text (@mode, #RRGGBB, %brightness, standard color)
 83          # input() will block until a newline is sent
 84          inText = input().strip()
 85          # Sometimes Windows sends an extra (or missing) newline - ignore them
 86          if inText == "":
 87              continue
 88          # Process the input text - start with the presets (no #,@,etc)
 89          # We use startswith to not have to worry about CR vs CR+LF differences
 90          if inText.lower().startswith("red"):
 91              # set the target color to red
 92              targetColor = (255, 0, 0)
 93              # and set the mode to solid if we're in a mode that ignores targetColor
 94              if mode == "wheel":
 95                  mode="solid"
 96          # similar for green, yellow, and black
 97          elif inText.lower().startswith("green"):
 98              targetColor = (0, 255, 0)
 99              if mode == "wheel":
100                  mode="solid"
101          elif inText.lower().startswith("yellow"):
102              targetColor = (200, 200, 0)
103              if mode == "wheel":
104                  mode="solid"
105          elif inText.lower().startswith("black"):
106              targetColor = (0, 0, 0)
107              if mode == "wheel":
108                  mode="solid"
109          # Here we're going to change the mode - which starts w/@
110          elif inText.lower().startswith("@"):
111              mode= inText[1:]
112          # Here we can set the brightness with a "%" symbol
113          elif inText.startswith("%"):
114              pctText = inText[1:]
115              pct = float(pctText)/100.0
116              pixels.brightness=pct
117          # If we get a hex code set it and go to solid
118          elif inText.startswith("#"):
119              hexcode = inText[1:]
120              targetColor = hex2rgb(hexcode)
121              if mode == "wheel":
122                  mode="solid"
123          # if we get a command we don't understand, set it to gray
124          # we should probably just ignore it but this helps debug
125          else:
126              targetColor =(50, 50, 50)
127              if mode == "wheel":
128                  mode="solid"
129      else:
130          # If no text available, update the color according to the mode
131          if mode == 'blink':
132              if curColor == black:
133                  curColor = targetColor
134              else:
135                  curColor = black
136              sleep(.4)
137              # print('.', end='')
138              pixels.fill(curColor)
139              pixels.show()
140          elif mode == 'wheel':
141              sleep(.05)
142              pos = (pos + 1) % 255
143              pixels.fill(colorwheel(pos))
144              pixels.show()
145          elif mode == 'solid':
146              pixels.fill(targetColor)
147              pixels.show()
148          elif mode == 'beat':
149              pos = (pos + 5 ) % 106
150              scaleAvg = (beatArray[(pos-2)%106] + beatArray[(pos-1)%106] + beatArray[pos] +
151                          beatArray[(pos+1)%106] + beatArray[(pos+2)%106])/5
152              beatColor = tuple(int(scaleAvg*x) for x in targetColor)
153              pixels.fill(beatColor)
154              sleep(.025)
155              pixels.show()
156          elif mode == 'ramp':
157              pos = ((pos + 5 ) % 255)
158              scaleFactor = (2*abs(pos-127))/255
159              beatColor = tuple(int(scaleFactor * x) for x in targetColor)
160              pixels.fill(beatColor)
161              sleep(.075)
162              pixels.show()