Once you've finished setting up your RP2040 Prop-Maker Feather with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.
# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import time
import board
import audiocore
import audiobusio
import audiomixer
import pwmio
from digitalio import DigitalInOut, Direction, Pull
import neopixel
from adafruit_ticks import ticks_ms, ticks_add, ticks_diff
from adafruit_led_animation.animation.pulse import Pulse
from adafruit_led_animation.animation.rainbow import Rainbow
from adafruit_led_animation.color import RED
from adafruit_motor import servo
import adafruit_lis3dh
time.sleep(2)
# enable external power pin
# provides power to the external components
external_power = DigitalInOut(board.EXTERNAL_POWER)
external_power.direction = Direction.OUTPUT
external_power.value = True
# i2s playback
wave_file = open("wand-mix-sfx.wav", "rb")
wave = audiocore.WaveFile(wave_file)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(voice_count=1, sample_rate=22050, channel_count=1,
bits_per_sample=16, samples_signed=True)
audio.play(mixer)
mixer.voice[0].play(wave, loop=True)
mixer.voice[0].level = 0
# servo control
pwm = pwmio.PWMOut(board.EXTERNAL_SERVO, frequency=50)
prop_servo = servo.ContinuousServo(pwm)
servo_move = False
# external button
switch = DigitalInOut(board.EXTERNAL_BUTTON)
switch.direction = Direction.INPUT
switch.pull = Pull.UP
switch_state = False
# external neopixels
num_pixels = 24
pixels = neopixel.NeoPixel(board.EXTERNAL_NEOPIXELS, num_pixels)
pixels.brightness = 0.3
rainbow = Rainbow(pixels, speed=0.05, period=2)
pulse = Pulse(pixels, speed=0.1, color=RED, period=3)
i2c = board.I2C()
int1 = DigitalInOut(board.ACCELEROMETER_INTERRUPT)
lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c, int1=int1)
lis3dh.range = adafruit_lis3dh.RANGE_2_G
clock = ticks_ms()
prop_time = 3000
while True:
if not servo_move:
pulse.animate()
mixer.voice[0].level = 0.0
prop_servo.throttle = 0.0
else:
prop_servo.throttle = 0.5
rainbow.animate()
mixer.voice[0].level = 0.5
if ticks_diff(ticks_ms(), clock) >= prop_time:
servo_move = False
if lis3dh.shake(shake_threshold=20) or not switch.value and switch_state is False:
servo_move = True
clock = ticks_ms()
clock = ticks_add(clock, prop_time)
switch_state = True
if switch.value and switch_state is True:
switch_state = False
Upload the Code and Libraries to the RP2040 Prop-Maker Feather
After downloading the Project Bundle, plug your RP2040 Prop-Maker Feather into the computer's USB port with a known good USB data+power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the RP2040 Prop-Maker Feather's CIRCUITPY drive.
- lib folder
- code.py
- wand-mix-sfx.wav
Your RP2040 Prop-Maker Feather CIRCUITPY drive should look like this after copying the lib folder, the code.py file and wand-mix-sfx.wav audio file.
How the CircuitPython Code Works
The code begins by enabled the EXTERNAL_POWER pin. This pin enables power to all of the external component pins, which includes the servo header, external NeoPixel pin, external button pin and speaker output.
# enable external power pin # provides power to the external components external_power = DigitalInOut(board.EXTERNAL_POWER) external_power.direction = Direction.OUTPUT external_power.value = True
Audio Setup
Next is I2S audio setup. The wand-mix-sfx.wav audio file is passed to a Mixer object. The Mixer is setup to play the sound effect in a loop, but with the volume initially at 0. Later on in the code loop, the volume will be adjusted so that you can hear the audio. This is one technique you can use to incorporate playing and pausing audio in a non-blocking way in your code.
# i2s playback
wave_file = open("wand-mix-sfx.wav", "rb")
wave = audiocore.WaveFile(wave_file)
audio = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA)
mixer = audiomixer.Mixer(voice_count=1, sample_rate=22050, channel_count=1,
bits_per_sample=16, samples_signed=True)
audio.play(mixer)
mixer.voice[0].play(wave, loop=True)
mixer.voice[0].level = 0
Servo and Button
A continuous servo and button are setup next using the servo header and the external button pin.
# servo control pwm = pwmio.PWMOut(board.EXTERNAL_SERVO, frequency=50) prop_servo = servo.ContinuousServo(pwm) servo_move = False # external button switch = DigitalInOut(board.EXTERNAL_BUTTON) switch.direction = Direction.INPUT switch.pull = Pull.UP switch_state = False
NeoPixels and Animations
The NeoPixel ring is setup on the external NeoPixel pin. The NeoPixels are using the Rainbow and Pulse animations from the LED animations library.
# external neopixels num_pixels = 24 pixels = neopixel.NeoPixel(board.EXTERNAL_NEOPIXELS, num_pixels) pixels.brightness = 0.3 rainbow = Rainbow(pixels, speed=0.05, period=2) pulse = Pulse(pixels, speed=0.1, color=RED, period=3)
Accelerometer
Last but not least, the onboard LIS3DH is initialized over I2C. In the loop, the LIS3DH will be able to affect the prop by detecting shake.
i2c = board.I2C() int1 = DigitalInOut(board.ACCELEROMETER_INTERRUPT) lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c, int1=int1) lis3dh.range = adafruit_lis3dh.RANGE_2_G
The Loop
In the loop the servo_move state determines what all of the components are doing. If servo_move is False, then the NeoPixels pulse red, the speaker is silent and the servo does not move.
if not servo_move:
pulse.animate()
mixer.voice[0].level = 0.0
prop_servo.throttle = 0.0
If the LIS3DH detects a shake or the external button is pressed, then servo_move is set to True.
if lis3dh.shake(shake_threshold=20) or not switch.value and switch_state is False:
servo_move = True
clock = ticks_ms()
clock = ticks_add(clock, prop_time)
switch_state = True
if switch.value and switch_state is True:
switch_state = False
When servo_move is True, the NeoPixels show the rainbow animation, the servo spins and the speaker output is set to 0.5 with the Mixer object. ticks is used to keep time in a non-blocking way. After three seconds have passed, then servo_move is set back to False.
else:
prop_servo.throttle = 0.5
rainbow.animate()
mixer.voice[0].level = 0.5
if ticks_diff(ticks_ms(), clock) >= prop_time:
servo_move = False
Page last edited January 21, 2025
Text editor powered by tinymce.