This is the full version of the code with capacitive touch functionality. If you're making the lights-only version, head to the MakeCode page for software instructions.
It's always a good idea to get your software loaded onto your board before the build. That way you'll be able to test your solder joints at each step of the way, and you'll get instant gratification when you plug in the lights.
Getting the software loaded is a 3-step process:
- Install the operating system (CircuitPython) on the board
- Copy the required libraries into the /lib folder on the board
- Save the code.py file to the board
CircuitPython is a fairly new OS that's changing rapidly. New features are being added and bugs are being fixed all the time, so it's always best to get a fresh version of CircuitPython and the library files before coding.
Install CircuitPython
The Circuit Playground Bluefruit ships with CircuitPython, but let's go ahead and update it to the latest version. It's super easy with the circuitpython.org website. Follow the directions on the CircuitPlayground BlueFruit Guide, or click the button below for a quick link to the download.
Download the file, plug your Circuit Playground into your computer via the USB port, and double-click the reset button. You'll see a drive appear called CPLAYBTBOOT. Drag the .uf2 file you just downloaded onto this drive to install CircuitPython.
You'll know it worked if the CPLAYBTBOOT drive name changes to CIRCUITPY.
Adafruit Circuit Python Libraries
Download the CircuitPython library bundle per the Circuit Playground Bluefruit instructions here. Unzip the files into a folder on your computer. Create a new folder on the CIRCUITPY drive and name it lib. The following libraries are required to run the code properly.
- adafruit_bus_device (directory)
- adafruit_circuitplayground (directory)
- adafruit_led_animation (directory)
- adafruit_lis3dh.mpy
- adafruit_mpr121.mpy
- adafruit_thermistor.mpy
- neopixel.mpy
Find all these files in the library bundle and copy them into the lib file you just made on your CIRCUITPY drive.
Upload Files
Click the link below to download the project zip – This contains the code and audio files. Upload the code.py file to the CIRCUITPY drive root (main) folder.
Create a new folder on the CIRCUITPY drive and name it sounds. Upload the audio files to that folder. The code will run properly when all of the files have been uploaded.
Check out the image above to see what your CIRCUITPY drive should look like when all the files are in place.
# SPDX-FileCopyrightText: 2020 Erin St Blaine for Adafruit Industries # SPDX-FileCopyrightText: 2020 Limor Fried for Adafruit Industries # # SPDX-License-Identifier: MIT """ Bottle Piano with Capacitive Touch Adafruit invests time and resources providing this open source code. Please support Adafruit and open source hardware by purchasing products from Adafruit! Written by Melissa LeBlanc, Erin St Blaine & Limor Fried for Adafruit Industries Copyright (c) 2019-2020 Adafruit Industries Licensed under the MIT license. All text above must be included in any redistribution. """ import time import digitalio import board import neopixel import busio import adafruit_mpr121 from audiocore import WaveFile from audiopwmio import PWMAudioOut as AudioOut from adafruit_led_animation.animation.rainbow import Rainbow from adafruit_led_animation.animation.rainbowchase import RainbowChase from adafruit_led_animation.animation.chase import Chase from adafruit_led_animation.animation.rainbowcomet import RainbowComet from adafruit_led_animation.sequence import AnimationSequence from adafruit_led_animation.color import ( BLACK, RED, ORANGE, YELLOW, GREEN, BLUE, MAGENTA, PURPLE, AMBER, TEAL, ) from adafruit_debouncer import Debouncer # pylint: disable=global-statement # NeoPixel strip setup -- set your total number of pixels here ----------------- PIXEL_NUM = 200 pixel_pin = board.A1 pixels = neopixel.NeoPixel(pixel_pin, PIXEL_NUM, brightness=1, auto_write=False) LAST_BUTTON = None ''' Customize your light strip for individual notes. Each line represents a bottle. Change the numbers to reflect the first and last pixel in each ring. You can also change the colors assigned to each bottle here. ''' bottle_lights = ( (0, 10, RED), (15, 30, ORANGE), (31, 52, AMBER), (53, 72, YELLOW), (77, 96, GREEN), (98, 119, TEAL), (120, 145, BLUE), (150, 173, PURPLE), (180, 200, MAGENTA), ) # Cap touch board setup ------------------------------------------------------ i2c = busio.I2C(board.SCL, board.SDA) mpr121 = adafruit_mpr121.MPR121(i2c) # Demo MODE LED Animations ------------------------------------------------------ rainbow = Rainbow(pixels, speed=0, period=10, name="rainbow", step=1) rainbow_chase = RainbowChase(pixels, speed=0, size=5, spacing=10) chase = Chase(pixels, speed=0.1, color=RED, size=1, spacing=6) rainbow_comet = RainbowComet(pixels, speed=0.01, tail_length=60, bounce=True) # Animation Sequence Playlist -- rearrange to change the order of animations animations = AnimationSequence( rainbow_chase, chase, rainbow_comet, auto_clear=True, auto_reset=True, ) def go_dark(): '''set all pixels to black''' pixels.fill(BLACK) pixels.show() # Debouncer ------------------------------------------------------ buttons = [Debouncer(mpr121[i]) for i in range(12)] # Audio Setup ------------------------------------------------------ spkr_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) spkr_enable.direction = digitalio.Direction.OUTPUT spkr_enable.value = True audio = AudioOut(board.SPEAKER) tracks = ( WaveFile(open("sounds/F2.wav", "rb")), # 0 WaveFile(open("sounds/G2.wav", "rb")), # 1 WaveFile(open("sounds/A2.wav", "rb")), # 2 WaveFile(open("sounds/Bb2.wav", "rb")), # 3 WaveFile(open("sounds/C2.wav", "rb")), # 4 WaveFile(open("sounds/D3.wav", "rb")), # 5 WaveFile(open("sounds/E3.wav", "rb")), # 6 WaveFile(open("sounds/F3.wav", "rb")), # 7 WaveFile(open("sounds/F1.wav", "rb")), # 7 WaveFile(open("sounds/G1.wav", "rb")), # 8 WaveFile(open("sounds/A1.wav", "rb")), # 9 WaveFile(open("sounds/Bb1.wav", "rb")), # 10 WaveFile(open("sounds/C1.wav", "rb")), # 11 WaveFile(open("sounds/D2.wav", "rb")), # 12 WaveFile(open("sounds/E2.wav", "rb")), # 13 WaveFile(open("sounds/F2.wav", "rb")), # 13 ) # Add or change song track names here. They will play in the order listed. demo_tracks = ( WaveFile(open("sounds/undersea.wav", "rb")), WaveFile(open("sounds/tequila.wav", "rb")), WaveFile(open("sounds/lion.wav", "rb")), ) MODE = 0 # Initial mode = OFF SONG = 0 def light_up(bottle): '''light up the bottles''' lights = bottle_lights[bottle] for pixel_id in range(lights[0], lights[1]): pixels[pixel_id] = lights[2] def play_bottle(bottle_id, is_octave): ''' play audio tracks and light up bottles''' global MODE go_dark() light_up(bottle_id) if is_octave: audio.play(tracks[bottle_id + 7]) # Start playing sound light_up(8) else: audio.play(tracks[bottle_id]) # Start playing sound pixels.show() MODE = 2 def check_buttons(touched): ''' check to see if buttons have been pressed''' global MODE, LAST_BUTTON octave = touched[11] off = touched[9] if octave: light_up(8) for pad in range(1, 9): if LAST_BUTTON is not None and not touched[LAST_BUTTON]: LAST_BUTTON = None if pad != LAST_BUTTON and touched[pad]: LAST_BUTTON = pad play_bottle(pad - 1, octave) if off: MODE = 9 go_dark() if touched[10]: go_dark() audio.play(demo_tracks[SONG]) while audio.playing: animations.animate() MODE = 3 while True: # Idle mode: Play a Rainbow animation when nothing's being touched if MODE == 0: pixels.brightness = 0.3 #rainbow mode is much brighter than the other modes, so adjust here rainbow.animate() for button in buttons: button.update() check_buttons(mpr121.touched_pins) time.sleep(0.1) for i in range(12): if buttons[i].fell: MODE = 1 # If not idle mode if MODE >= 1: pixels.brightness = 1 check_buttons(mpr121.touched_pins) time.sleep(0.1) if MODE == 2: # mode 2 is individual notes if audio.playing: pixels.show() while audio.playing: check_buttons(mpr121.touched_pins) time.sleep(0.07) else: MODE = 0 # Return to idle mode if MODE == 3: SONG = SONG + 1 animations.next() if SONG == 3: MODE = 0 SONG = 0 else: MODE = 0 # Return to idle mode if MODE == 9: # MODE 9 is "off" mode, listening for a new button press to wake up. # for button in buttons: # button.update() for i in range(12): if buttons[i].fell: MODE = 1
Customizing Your Code
The best way to edit and upload your code is with the Mu Editor, a simple Python editor that works with Adafruit CircuitPython hardware. It's written in Python and works on Windows, MacOS, Linux and Raspberry Pi. The serial console is built right in so you get immediate feedback from your board's serial output. Instructions for installing Mu is here.
NeoPixel Setup
Look near the top of the code to find PIXEL_NUM
. Change this number to reflect your total number of LEDs in your NeoPixel strips.
# NeoPixel strip setup -- set your total number of pixels here ----------------- PIXEL_NUM = 200 pixel_pin = board.A1 pixels = neopixel.NeoPixel(pixel_pin, PIXEL_NUM, brightness=1, auto_write=False) LAST_BUTTON = None
Next we'll set up the pixel mapping for the individual bottles. Starting from 0, count out the pixels in the section of LED strip below each bottle. Replace my numbers with yours -- the first number is the first pixel in the section and the second number is the last pixel in the section.
If you want to customize the colors you can do that here as well. Remember to import any additional colors you want to add in the list near the top of the code.
This code is based on the CircuitPython LED Animations code library by Kattni Rembor. Check out that guide for a lot more info on using this library.
''' Customize your light strip for individual notes. Each line represents a bottle. Change the numbers to reflect the first and last pixel in each ring. You can also change the colors assigned to each bottle here. ''' bottle_lights = ( (0, 10, RED), (15, 30, ORANGE), (31, 52, AMBER), (53, 72, YELLOW), (77, 96, GREEN), (98, 119, TEAL), (120, 145, BLUE), (150, 173, PURPLE), (180, 200, MAGENTA), )
Demo Mode Animations
Customize the animations that play in idle mode and demo mode here. Play with the settings until the animations look exactly the way you want. Or try out some other animations -- there are a lot more plug-and-play animations and ideas in the LED animations guide.
The lower section is your animation playlist. Rearrange the order so they go with your songs.
# Demo MODE LED Animations ------------------------------------------------------ rainbow = Rainbow(pixels, speed=0.1, period=10, name="rainbow", step=1) rainbow_chase = RainbowChase(pixels, speed=0, size=5, spacing=10) chase = Chase(pixels, speed=0.1, color=RED, size=1, spacing=6) rainbow_comet = RainbowComet(pixels, speed=0.01, tail_length=60, bounce=True) # Animation Sequence Playlist -- rearrange to change the order of animations animations = AnimationSequence( rainbow_chase, chase, rainbow_comet, auto_clear=True, auto_reset=True, )
Audio Files
We've included two octaves of bottle tones in the key of F, as well as three demo tracks. This castle can play a few bars of Under the Sea (from the Little Mermaid movie), Tequila, and The Lion Sleeps Tonight. It's not hard to add your own favorite melodies, but you'll need to keep them fairly short - there is 2MB of memory on this board and we've just about filled it up with all these files.
Adafruit CircuitPython supports 16-bit, Mono, 22.050kHz .wav audio format. See this guide to help format any audio files you might want to use in this project besides the files provided.
tracks = ( WaveFile(open("sounds/F1.wav", "rb")), # 0 WaveFile(open("sounds/G1.wav", "rb")), # 1 WaveFile(open("sounds/A1.wav", "rb")), # 2 WaveFile(open("sounds/Bb1.wav", "rb")), # 3 WaveFile(open("sounds/C1.wav", "rb")), # 4 WaveFile(open("sounds/D2.wav", "rb")), # 5 WaveFile(open("sounds/E2.wav", "rb")), # 6 WaveFile(open("sounds/F2.wav", "rb")), # 7 WaveFile(open("sounds/G2.wav", "rb")), # 8 WaveFile(open("sounds/A2.wav", "rb")), # 9 WaveFile(open("sounds/Bb2.wav", "rb")), # 10 WaveFile(open("sounds/C2.wav", "rb")), # 11 WaveFile(open("sounds/D3.wav", "rb")), # 12 WaveFile(open("sounds/E3.wav", "rb")), # 13 WaveFile(open("sounds/F3.wav", "rb")), # 13 ) # Add or change song track names here. They will play in the order listed. demo_tracks = ( WaveFile(open("sounds/undersea.wav", "rb")), WaveFile(open("sounds/tequila.wav", "rb")), WaveFile(open("sounds/lion.wav", "rb")), )
Adding your Own Wav Files
I found that even following the very specific guidelines for .wav file setup, some files would crash the Feather when triggered. If you're having this problem, head to this guide and re-crunch your files using Audacity.
This is a teeny tiny speaker, so sound files with more high-end frequencies will sound a lot better than deep, booming sounds. Sound files that have a lot of (or even any) low frequencies do not sound good when played back over small speakers. Too much low end will cause a small speaker to distort. To optimize your sound files for small speaker playback use a High Pass Filter (HPF) in your preferred audio editing software. Rolling off low frequencies below 250 Hz is a good starting point.
Audacity is great and simple audio editing app, and best of all, it’s free!
Page last edited January 21, 2025
Text editor powered by tinymce.