Overview
Magikoopa Wand
You can build Kamek's Magic Wand from Super Mario Bros!
We designed and 3D printed a magic wand inspired by Kamek the Magikoopa in the Super Mario Brothers movie. It’s got motion activated lights and sound with a spinning crystal gem.
Powered by the Adafruit RP2040 Prop-Maker Feather, this advanced prop uses an accelerometer to detect swings and hits that trigger super bright NeoPixels with full sound effects!
Prop-Maker Feather
The Prop-Maker Feather was designed for creating advanced props using motion, lights and sound. The LIS3DH accelerometer can detect steps, swings and hits. It has an on-board class-D audio amp for blasting sound effects. For creating stunning lighting effects, the built-in NeoPixel driver and 3W RGB LED driver are essential.
Terminal Block Port - With easy-to-use screw terminals you can quickly connect and disconnect, lights, speakers and buttons!
Servo Connection - Plug any hobby servo with 3 wires into the 0.1" spaced header, and you can have quick motion control!
Page last edited March 08, 2024
Text editor powered by tinymce.
Circuit Diagram
Adafruit Library for Fritzing
The wiring diagram below provides a visual reference for connecting the components. It is not true to scale, it is just meant to be used as reference. This diagram was created using the Fritzing software package.
Take a moment to review the components in the circuit diagram. This illustration is meant for referencing wired connections - the length of wire, position and size of components are not exact.
Page last edited March 08, 2024
Text editor powered by tinymce.
CircuitPython
CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-cost microcontrollers. It makes it easier than ever to get prototyping by requiring no upfront desktop software downloads. Simply copy and edit files on the CIRCUITPY drive to iterate.
CircuitPython Quickstart
Follow this step-by-step to quickly get CircuitPython running on your board.
Click the link above to download the latest CircuitPython UF2 file.
Save it wherever is convenient for you.
To enter the bootloader, hold down the BOOT/BOOTSEL button (highlighted in red above), and while continuing to hold it (don't let go!), press and release the reset button (highlighted in red or blue above). Continue to hold the BOOT/BOOTSEL button until the RPI-RP2 drive appears!
If the drive does not appear, release all the buttons, and then repeat the process above.
You can also start with your board unplugged from USB, press and hold the BOOTSEL button (highlighted in red above), continue to hold it while plugging it into USB, and wait for the drive to appear before releasing the button.
A lot of people end up using charge-only USB cables and it is very frustrating! Make sure you have a USB cable you know is good for data sync.
You will see a new disk drive appear called RPI-RP2.
Drag the adafruit_circuitpython_etc.uf2 file to RPI-RP2.
The RPI-RP2 drive will disappear and a new disk drive called CIRCUITPY will appear.
That's it, you're done! :)
Safe Mode
You want to edit your code.py or modify the files on your CIRCUITPY drive, but find that you can't. Perhaps your board has gotten into a state where CIRCUITPY is read-only. You may have turned off the CIRCUITPY drive altogether. Whatever the reason, safe mode can help.
Safe mode in CircuitPython does not run any user code on startup, and disables auto-reload. This means a few things. First, safe mode bypasses any code in boot.py (where you can set CIRCUITPY read-only or turn it off completely). Second, it does not run the code in code.py. And finally, it does not automatically soft-reload when data is written to the CIRCUITPY drive.
Therefore, whatever you may have done to put your board in a non-interactive state, safe mode gives you the opportunity to correct it without losing all of the data on the CIRCUITPY drive.
To enter safe mode when using CircuitPython, plug in your board or hit reset (highlighted in red above). Immediately after the board starts up or resets, it waits 1000ms. On some boards, the onboard status LED (highlighted in green above) will blink yellow during that time. If you press reset during that 1000ms, the board will start up in safe mode. It can be difficult to react to the yellow LED, so you may want to think of it simply as a slow double click of the reset button. (Remember, a fast double click of reset enters the bootloader.)
In Safe Mode
If you successfully enter safe mode on CircuitPython, the LED will intermittently blink yellow three times.
If you connect to the serial console, you'll find the following message.
Auto-reload is off. Running in safe mode! Not running saved code. CircuitPython is in safe mode because you pressed the reset button during boot. Press again to exit safe mode. Press any key to enter the REPL. Use CTRL-D to reload.
You can now edit the contents of the CIRCUITPY drive. Remember, your code will not run until you press the reset button, or unplug and plug in your board, to get out of safe mode.
Flash Resetting UF2
If your board ever gets into a really weird state and CIRCUITPY doesn't show up as a disk drive after installing CircuitPython, try loading this 'nuke' UF2 to RPI-RP2. which will do a 'deep clean' on your Flash Memory. You will lose all the files on the board, but at least you'll be able to revive it! After loading this UF2, follow the steps above to re-install CircuitPython.
Page last edited March 08, 2024
Text editor powered by tinymce.
Coding the Magic Wand
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 March 08, 2024
Text editor powered by tinymce.
3D Printing
Parts List
STL files for 3D printing are oriented to print "as-is" on FDM style machines. Parts are designed to 3D print without any support material. Original design source may be downloaded using the links below.
Slice with settings for PLA material
The parts were sliced using CURA using the slice settings below.
- PLA filament 220c extruder
- 0.2 layer height
- 10% gyroid infill
- 60mm/s print speed
- 60c heated bed
Page last edited March 08, 2024
Text editor powered by tinymce.
Assemble
Amp Gain
Bridge the 12db pad and center pads with a bit of solder to increase the amplifier volume.
Solder Slide Switch
Measure a short wire and solder the two pins on the slide switch to EN and GND pins on the Feather.
Speaker Extension
Use a JST 2 pin socket extension to connect the speaker. Attach to the + and - to the terminal blocks.
Button Wires
Attach wires to the 5V, GND and Button terminal blocks.
Feather mount
Align the pins on the feather mount to the screw holes next to the terminals. Use M2.5x5mm screws on the other two screw holes.
Attach Feather to Base
Use two M2.5x6mm screws to attach the Feather mount to the base part.
NeoPixel Ring Wires
Measure and cut wires to connect the Data Input, 5V and GND to the 24 NeoPixel ring.
Mount NeoPixel Ring
Align the ring to the mount on the base part. Pass wires through the cut out on the base. Connect NeoPixel wires by sharing the 5v and GND terminal.
Mount Servo
Align the servo wire to the cutout on the base part. Pass the cable through the cutout on the base and connect to the header pins on the Feather.
Mount Battery
Orient the battery with the cable facing up.
Speaker Mount
Use M2.5x6mm screws to secure the speaker mount to the base part.
Attach Speaker
Press fit the speaker base into the mount.
Battery JST extension cable
Connect an 2 pin JST extension cable to the battery.
Pass Button wires
Align the USB port to the cutout on the handle part. Pass button wires through the cutout.
Share Button Ground connection
Solder an additional button connector to the ground wire.
Connect Button wires
Attach the wires to each pin on the button.
Speaker and Battery Connections
Secure cables for the speaker and battery to the ports on the Feather.
Align base to handle
Coil wires around the speaker to fit in the handle.
Align the USB port to the cut out on the handle part.
Use M2.5x6mm screws to attach the base to the handle.
Page last edited March 08, 2024
Text editor powered by tinymce.