|
| 1 | +""" |
| 2 | +Bottle Piano with Capacitive Touch |
| 3 | +Adafruit invests time and resources providing this open source code. |
| 4 | +Please support Adafruit and open source hardware by purchasing |
| 5 | +products from Adafruit! |
| 6 | +Written by Melissa LeBlanc, Erin St Blaine & Limor Fried for Adafruit Industries |
| 7 | +Copyright (c) 2019-2020 Adafruit Industries |
| 8 | +Licensed under the MIT license. |
| 9 | +All text above must be included in any redistribution. |
| 10 | +""" |
| 11 | + |
| 12 | +import time |
| 13 | +import digitalio |
| 14 | +import board |
| 15 | +import neopixel |
| 16 | +import busio |
| 17 | +import adafruit_mpr121 |
| 18 | +from audiocore import WaveFile |
| 19 | +from audiopwmio import PWMAudioOut as AudioOut |
| 20 | +from adafruit_led_animation.animation.rainbow import Rainbow |
| 21 | +from adafruit_led_animation.animation.rainbowchase import RainbowChase |
| 22 | +from adafruit_led_animation.animation.chase import Chase |
| 23 | +from adafruit_led_animation.animation.rainbowcomet import RainbowComet |
| 24 | +from adafruit_led_animation.sequence import AnimationSequence |
| 25 | +from adafruit_led_animation.color import ( |
| 26 | + BLACK, |
| 27 | + RED, |
| 28 | + ORANGE, |
| 29 | + YELLOW, |
| 30 | + GREEN, |
| 31 | + BLUE, |
| 32 | + MAGENTA, |
| 33 | + PURPLE, |
| 34 | + AMBER, |
| 35 | + TEAL, |
| 36 | +) |
| 37 | +from adafruit_debouncer import Debouncer |
| 38 | + |
| 39 | +# pylint: disable=global-statement |
| 40 | + |
| 41 | +# NeoPixel strip setup -- set your total number of pixels here ----------------- |
| 42 | +PIXEL_NUM = 200 |
| 43 | +pixel_pin = board.A1 |
| 44 | +pixels = neopixel.NeoPixel(pixel_pin, PIXEL_NUM, brightness=1, auto_write=False) |
| 45 | +LAST_BUTTON = None |
| 46 | + |
| 47 | +''' |
| 48 | +Customize your light strip for individual notes. Each line represents a bottle. |
| 49 | +Change the numbers to reflect the first and last pixel in each ring. You can also |
| 50 | +change the colors assigned to each bottle here. |
| 51 | +''' |
| 52 | + |
| 53 | +bottle_lights = ( |
| 54 | + (0, 10, RED), |
| 55 | + (15, 30, ORANGE), |
| 56 | + (31, 52, AMBER), |
| 57 | + (53, 72, YELLOW), |
| 58 | + (77, 96, GREEN), |
| 59 | + (98, 119, TEAL), |
| 60 | + (120, 145, BLUE), |
| 61 | + (150, 173, PURPLE), |
| 62 | + (180, 200, MAGENTA), |
| 63 | +) |
| 64 | + |
| 65 | +# Cap touch board setup ------------------------------------------------------ |
| 66 | +i2c = busio.I2C(board.SCL, board.SDA) |
| 67 | +mpr121 = adafruit_mpr121.MPR121(i2c) |
| 68 | + |
| 69 | +# Demo MODE LED Animations ------------------------------------------------------ |
| 70 | +rainbow = Rainbow(pixels, speed=0.1, period=10, name="rainbow", step=1) |
| 71 | +rainbow_chase = RainbowChase(pixels, speed=0, size=5, spacing=10) |
| 72 | +chase = Chase(pixels, speed=0.1, color=RED, size=1, spacing=6) |
| 73 | +rainbow_comet = RainbowComet(pixels, speed=0.01, tail_length=60, bounce=True) |
| 74 | + |
| 75 | +# Animation Sequence Playlist -- rearrange to change the order of animations |
| 76 | + |
| 77 | +animations = AnimationSequence( |
| 78 | + rainbow_chase, |
| 79 | + chase, |
| 80 | + rainbow_comet, |
| 81 | + auto_clear=True, |
| 82 | + auto_reset=True, |
| 83 | +) |
| 84 | + |
| 85 | +def go_dark(): |
| 86 | + '''set all pixels to black''' |
| 87 | + pixels.fill(BLACK) |
| 88 | + pixels.show() |
| 89 | + |
| 90 | + |
| 91 | +# Debouncer ------------------------------------------------------ |
| 92 | + |
| 93 | +buttons = [Debouncer(mpr121[i]) for i in range(12)] |
| 94 | + |
| 95 | + |
| 96 | +# Audio Setup ------------------------------------------------------ |
| 97 | + |
| 98 | +spkr_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) |
| 99 | +spkr_enable.direction = digitalio.Direction.OUTPUT |
| 100 | +spkr_enable.value = True |
| 101 | + |
| 102 | +audio = AudioOut(board.SPEAKER) |
| 103 | + |
| 104 | +tracks = ( |
| 105 | + WaveFile(open("sounds/F1.wav", "rb")), # 0 |
| 106 | + WaveFile(open("sounds/G1.wav", "rb")), # 1 |
| 107 | + WaveFile(open("sounds/A1.wav", "rb")), # 2 |
| 108 | + WaveFile(open("sounds/Bb1.wav", "rb")), # 3 |
| 109 | + WaveFile(open("sounds/C1.wav", "rb")), # 4 |
| 110 | + WaveFile(open("sounds/D2.wav", "rb")), # 5 |
| 111 | + WaveFile(open("sounds/E2.wav", "rb")), # 6 |
| 112 | + WaveFile(open("sounds/F2.wav", "rb")), # 7 |
| 113 | + WaveFile(open("sounds/G2.wav", "rb")), # 8 |
| 114 | + WaveFile(open("sounds/A2.wav", "rb")), # 9 |
| 115 | + WaveFile(open("sounds/Bb2.wav", "rb")), # 10 |
| 116 | + WaveFile(open("sounds/C2.wav", "rb")), # 11 |
| 117 | + WaveFile(open("sounds/D3.wav", "rb")), # 12 |
| 118 | + WaveFile(open("sounds/E3.wav", "rb")), # 13 |
| 119 | + WaveFile(open("sounds/F3.wav", "rb")), # 13 |
| 120 | +) |
| 121 | + |
| 122 | +# Add or change song track names here. They will play in the order listed. |
| 123 | +demo_tracks = ( |
| 124 | + WaveFile(open("sounds/undersea.wav", "rb")), |
| 125 | + WaveFile(open("sounds/tequila.wav", "rb")), |
| 126 | + WaveFile(open("sounds/lion.wav", "rb")), |
| 127 | +) |
| 128 | + |
| 129 | +MODE = 0 # Initial mode = OFF |
| 130 | +SONG = 0 |
| 131 | + |
| 132 | + |
| 133 | + |
| 134 | + |
| 135 | +def light_up(bottle): |
| 136 | + '''light up the bottles''' |
| 137 | + lights = bottle_lights[bottle] |
| 138 | + for pixel_id in range(lights[0], lights[1]): |
| 139 | + pixels[pixel_id] = lights[2] |
| 140 | + |
| 141 | + |
| 142 | +def play_bottle(bottle_id, is_octave): |
| 143 | + ''' play audio tracks and light up bottles''' |
| 144 | + global MODE |
| 145 | + go_dark() |
| 146 | + light_up(bottle_id) |
| 147 | + if is_octave: |
| 148 | + audio.play(tracks[bottle_id + 7]) # Start playing sound |
| 149 | + light_up(9) |
| 150 | + else: |
| 151 | + audio.play(tracks[bottle_id]) # Start playing sound |
| 152 | + pixels.show() |
| 153 | + MODE = 2 |
| 154 | + |
| 155 | + |
| 156 | +def check_buttons(touched): |
| 157 | + ''' check to see if buttons have been pressed''' |
| 158 | + global MODE, LAST_BUTTON |
| 159 | + octave = touched[11] |
| 160 | + if octave: |
| 161 | + light_up(8) |
| 162 | + for pad in range(1, 9): |
| 163 | + if LAST_BUTTON is not None and not touched[LAST_BUTTON]: |
| 164 | + LAST_BUTTON = None |
| 165 | + if pad != LAST_BUTTON and touched[pad]: |
| 166 | + LAST_BUTTON = pad |
| 167 | + play_bottle(pad - 1, octave) |
| 168 | + if touched[9]: |
| 169 | + MODE = 9 |
| 170 | + go_dark() |
| 171 | + if touched[10]: |
| 172 | + go_dark() |
| 173 | + audio.play(demo_tracks[SONG]) |
| 174 | + while audio.playing: |
| 175 | + animations.animate() |
| 176 | + |
| 177 | + MODE = 3 |
| 178 | + |
| 179 | + |
| 180 | +while True: |
| 181 | + # Idle mode: Play a Rainbow animation when nothing's being touched |
| 182 | + if MODE == 0: |
| 183 | + pixels.brightness = 1 #rainbow mode is much brighter than the other modes, so adjust here |
| 184 | + rainbow.animate() |
| 185 | + for button in buttons: |
| 186 | + button.update() |
| 187 | + for i in range(12): |
| 188 | + if buttons[i].fell: |
| 189 | + MODE = 1 |
| 190 | + # If not idle mode |
| 191 | + if MODE >= 1: |
| 192 | + pixels.brightness = 1 |
| 193 | + check_buttons(mpr121.touched_pins) |
| 194 | + time.sleep(0.1) |
| 195 | + if MODE == 2: # mode 2 is individual notes |
| 196 | + if audio.playing: |
| 197 | + pixels.show() |
| 198 | + while audio.playing: |
| 199 | + check_buttons(mpr121.touched_pins) |
| 200 | + time.sleep(0.07) |
| 201 | + else: |
| 202 | + MODE = 0 # Return to idle mode |
| 203 | + if MODE == 3: |
| 204 | + SONG = SONG + 1 |
| 205 | + animations.next() |
| 206 | + if SONG == 3: |
| 207 | + MODE = 0 |
| 208 | + SONG = 0 |
| 209 | + |
| 210 | + else: |
| 211 | + MODE = 0 # Return to idle mode |
| 212 | + if MODE == 9: # MODE 9 is "off" mode, listening for a new button press to wake up. |
| 213 | + for button in buttons: |
| 214 | + button.update() |
| 215 | + for i in range(12): |
| 216 | + if buttons[i].fell: |
| 217 | + MODE = 1 |
0 commit comments