code.py
  1  # SPDX-FileCopyrightText: 2020 Anne Barela for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  import time
  6  import adafruit_dotstar
  7  import board
  8  import random
  9  import touchio
 10  from adafruit_pixel_framebuf import PixelFramebuffer
 11  from adafruit_led_animation.animation.rainbowchase import RainbowChase
 12  from adafruit_led_animation.animation.rainbowcomet import RainbowComet
 13  from adafruit_led_animation.animation.chase import Chase
 14  from adafruit_led_animation.color import PINK
 15  
 16  from adafruit_ble import BLERadio
 17  from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
 18  from adafruit_ble.services.nordic import UARTService
 19  from adafruit_bluefruit_connect.packet import Packet
 20  from adafruit_bluefruit_connect.color_packet import ColorPacket
 21  from adafruit_bluefruit_connect.button_packet import ButtonPacket
 22  
 23  ################################################################################
 24  # Customize variables
 25  
 26  # Set capacitive touch pin
 27  TOUCH_PIN = board.D11
 28  
 29  # These are the pixels covered by the brass cap touch
 30  # We will try to avoid using these pixels in the "twinkle" default animation
 31  COVERED_PIXELS = [40,41,42,48,49,50,56,57,58]
 32  
 33  # Adjust this higher if touch is too sensitive
 34  TOUCH_THRESHOLD = 3000
 35  
 36  # Adjust SCROLL_TEXT_COLOR_CHANGE_WAIT lower to make the color changes for
 37  # the text scroll animation faster
 38  SCROLL_TEXT_COLOR_CHANGE_WAIT = 5
 39  
 40  # Change this text that will be displayed when tapping 2 on the
 41  # Bluefruit app control pad (after connecting on your phone)
 42  SCROLL_TEXT_CUSTOM_WORD = "hello world"
 43  
 44  # Increase number to slow down scrolling
 45  SCROLL_TEXT_WAIT = 0.05
 46  
 47  # How bright each pixel in the default twinkling animation will be
 48  TWINKLE_BRIGHTNESS = 0.1
 49  
 50  ###############################################################################
 51  # Initialize hardware
 52  
 53  touch_pad = TOUCH_PIN
 54  touch = touchio.TouchIn(touch_pad)
 55  touch.threshold = TOUCH_THRESHOLD
 56  
 57  ble = BLERadio()
 58  uart_service = UARTService()
 59  advertisement = ProvideServicesAdvertisement(uart_service)
 60  
 61  # Colors
 62  YELLOW = (255, 150, 0)
 63  TEAL = (0, 255, 120)
 64  CYAN = (0, 255, 255)
 65  PURPLE = (180, 0, 255)
 66  TWINKLEY = (255, 255, 255)
 67  OFF = (0, 0, 0)
 68  
 69  # Setup Dotstar grid and pixel framebuffer for fancy animations
 70  pixel_width = 8
 71  pixel_height = 8
 72  num_pixels = pixel_width * pixel_height
 73  pixels = adafruit_dotstar.DotStar(board.A1, board.A2, num_pixels, auto_write=False, brightness=0.1)
 74  pixel_framebuf = PixelFramebuffer(
 75      pixels,
 76      pixel_width,
 77      pixel_height,
 78      rotation=1,
 79      alternating=False,
 80      reverse_x=True
 81  )
 82  # Fancy animations from https://learn.adafruit.com/circuitpython-led-animations
 83  rainbow_chase = RainbowChase(pixels, speed=0.1, size=3, spacing=6, step=8)
 84  chase = Chase(pixels, speed=0.1, color=CYAN, size=3, spacing=6)
 85  rainbow_comet = RainbowComet(pixels, speed=0.1, tail_length=5, bounce=True, colorwheel_offset=170)
 86  
 87  
 88  def scroll_framebuf_neg_x(word, color, shift_x, shift_y):
 89      pixel_framebuf.fill(0)
 90      color_int = int('0x%02x%02x%02x' % color, 16)
 91  
 92      # negate x so that the word can be shown from left to right
 93      pixel_framebuf.text(word, -shift_x, shift_y, color_int)
 94      pixel_framebuf.display()
 95      time.sleep(SCROLL_TEXT_WAIT)
 96  
 97  def scroll_text(packet, word):
 98      # scroll through entire length of string.
 99      # each letter is always 5 pixels wide, plus 1 space per letter
100      scroll_len = (len(word) * 5) + len(word)
101      color_list = [CYAN, TWINKLEY, PINK, PURPLE, YELLOW]
102  
103      color_i = 0
104      color_wait_tick = 0
105      # start the scroll from off the grid at -pixel_width
106      for x_pos in range(-pixel_width, scroll_len):
107          # detect touch
108          if touch.value:
109              pixel_framebuf.fill(0)
110              pixel_framebuf.display()
111              return;
112  
113          # detect new packet
114          if isinstance(packet, ButtonPacket) and packet.pressed:
115              return;
116  
117          color = color_list[color_i]
118          scroll_framebuf_neg_x(word, color, x_pos, 0)
119  
120          # Only change colors after SCROLL_TEXT_COLOR_CHANGE_WAIT
121          color_wait_tick = color_wait_tick + 1
122          if color_wait_tick == SCROLL_TEXT_COLOR_CHANGE_WAIT:
123              color_i = color_i + 1
124              color_wait_tick = 0
125  
126          if color_i == len(color_list):
127              color_i=0
128  
129      # wait a bit before scrolling again
130      time.sleep(.5)
131  
132  # Manually chosen pixels to display "Y"
133  # in the proper orientation
134  def yes(color):
135      pixels[26] = color
136      pixels[27] = color
137      pixels[28] = color
138      pixels[36] = color
139      pixels[44] = color
140      pixels[21] = color
141      pixels.show()
142      time.sleep(0.1)
143      pixels.fill(0)
144  
145  # Manually chosen pixels to display "N"
146  # in the proper orientation
147  def no(color):
148      pixels[26] = color
149      pixels[19] = color
150      pixels[12] = color
151      pixels[27] = color
152      pixels[28] = color
153      pixels[29] = color
154      pixels[30] = color
155      pixels[37] = color
156      pixels[44] = color
157      pixels.show()
158      time.sleep(0.1)
159      pixels.fill(0)
160  
161  def yes_or_no():
162      pixels.fill(0)
163      print(touch.raw_value)
164      value = 0
165      pick=0
166  
167      pick = random.randint(0,64)
168      time.sleep(0.1)
169  
170      if pick % 2:
171          print('picked yes!');
172          yes(PINK)
173          time.sleep(1)
174      else:
175          print('picked no!');
176          no(TEAL)
177          time.sleep(1)
178  
179  
180  def twinkle_show():
181      pixels.brightness = TWINKLE_BRIGHTNESS
182      pixels.show()
183      time.sleep(.1)
184      if touch.value:
185          return;
186  
187  def twinkle():
188      # randomly choose 3 pixels
189      spark1 = random.randint(0, num_pixels-1)
190      spark2 = random.randint(0, num_pixels-1)
191      spark3 = random.randint(0, num_pixels-1)
192  
193      # make sure that none of the chosen pixels are covered
194      while spark1 in COVERED_PIXELS:
195          spark1 = random.randint(0, num_pixels-1)
196      while spark2 in COVERED_PIXELS:
197          spark2 = random.randint(0, num_pixels-1)
198      while spark3 in COVERED_PIXELS:
199          spark3 = random.randint(0, num_pixels-1)
200  
201      # Control when chosen pixels turn on for dazzling effect
202      pixels[spark1] = TWINKLEY
203      pixels[spark2] = OFF
204      pixels[spark3] = OFF
205      twinkle_show()
206      pixels[spark1] = TWINKLEY
207      pixels[spark2] = TWINKLEY
208      pixels[spark3] = OFF
209      twinkle_show()
210      pixels[spark1] = TWINKLEY
211      pixels[spark2] = TWINKLEY
212      pixels[spark3] = TWINKLEY
213      twinkle_show()
214      pixels[spark1] = OFF
215      pixels[spark2] = TWINKLEY
216      pixels[spark3] = TWINKLEY
217      twinkle_show()
218      pixels[spark1] = OFF
219      pixels[spark2] = OFF
220      pixels[spark3] = TWINKLEY
221      twinkle_show()
222  
223      pixels.fill(OFF)
224      pixels.show()
225      time.sleep(0.6)
226  
227  # Initial empty state
228  state = ""
229  
230  while True:
231      # Advertise when not connected.
232      ble.start_advertising(advertisement)
233  
234      while not ble.connected:
235          if touch.value:
236              yes_or_no()
237          else:
238              twinkle()
239  
240      while ble.connected:
241          # Set the state
242          if uart_service.in_waiting:
243              # Packet is arriving.
244              packet = Packet.from_stream(uart_service)
245  
246              # set state string based on pressed button from Bluefruit app
247              # and to prevent redundant hits
248              if isinstance(packet, ButtonPacket) and packet.pressed:
249                  # UP button pressed
250                  if packet.button == ButtonPacket.UP and state != "chase":
251                      state = "chase"
252                  # DOWN button
253                  elif packet.button == ButtonPacket.DOWN and state != "comet":
254                      state = "comet"
255                  # 1 button
256                  elif packet.button == '1' and state != "rainbowchase":
257                      state = "rainbowchase"
258                  # 2 button
259                  elif packet.button == '2' and state != "hello":
260                      state = "hello"
261  
262          # Touch is handled as an interrupt state
263          if touch.value:
264              yes_or_no()
265  
266          # Act upon the state
267          if state == "chase":
268              chase.animate()
269          elif state == "comet":
270              rainbow_comet.animate()
271          elif state == "rainbowchase":
272              rainbow_chase.animate()
273          elif state == "hello":
274              pixels.fill(0)
275              scroll_text(packet, SCROLL_TEXT_CUSTOM_WORD)
276          else:
277              chase.animate()