/ ItsyBitsy_DotStar_Necklace / code.py
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()