In this project we’re building a Prop Maker Keyblade! Keyblades are sword like weapons featured in the Kingdom Hearts video game series. This design features the Adafruit Prop-Maker FeatherWing and NeoPixel LEDs to create a unique prop with motion activated sound effects. All of the parts are 3D printed and the electronics are from Adafruit.

Assembly

I designed this to be modular so parts snap fit together. This makes it easy to take apart so it’s ready for transport. The handle and pipes connect with screw-threaded ends. The electronics are easy to get to so the battery can easily be recharged or the code updated over USB. The blade is also detachable so it's easy to break it down and put it up for storage.

NeoPixel Props

This utilizes dual extrusion to create multi-colored elements that are printed using glitter infused filament. Parts printed with transparent PLA will diffuse the LEDs to illuminate specific features throughout the build. It's designed to house all of the electronics. This design was inspired by Dave Lunar’s Iron Warrior Battle Axe - It’s a take on a space marines weapon from warhammer 40k world.

Prerequisite Guides

If you're new to Adafruit Feather M4 Express, CircuitPython or soldering, take a moment to walk through the following guides to get you started.

Hardware Supplies

Circuit Diagram

This provides a visual reference for wiring of the components. They aren't true to scale, just meant to be used as reference. This diagrams was created using Fritzing software.

Adafruit Library for Fritzing

Use our Fritzing parts library to create circuit diagrams for your projects. Download the library or just grab the individual parts. Get library and parts from GitHub Adafruit Fritzing Parts.

Wired Connections

The Prop-Maker FeatherWing is fitted on top of Adafruit Feather M4 Express via short female/male headers. The speaker is connected via a molex pico blade connector.

Powering

The Adafruit Feather M4 Express can be powered via USB or JST using a 3.7v lipo battery. In this project, a 4400mAh lipo battery is used. The lipo battery is rechargeable via the USB port on the Adafruit Feather. The switch is wired to the enable and ground pins on the Prop-Maker FeatherWing. 

NeoPixel Ring

  • Ground pin to NeoPixel Port (GND pin) on Prop-Maker FeatherWing
  • Data In pin to NeoPixel Port (D5 pin) on Prop-Maker FeatherWing
  • Voltage pin to NeoPixel Port (3V pin) on Prop-Maker NeoPixel Port

NeoPixel Strip

  • Ground pin to Ground pin on NeoPixel ring
  • Data In pin to Data Out pin on NeoPixel ring
  • Voltage pin to Voltage pin on NeoPixel ring

Speaker

  • Voltage and ground to Prop-Maker speaker port

Toggle Switch

  • Middle pin to enable pin on Prop-Maker
  • Far left or right pin to ground on Prop-Maker

Install CircuitPython

The Adafruit Feather M4 ships with CircuitPython but lets go ahead and update it to the latest version. It's super easy with the circuitpython.org website, just click the link below to launch the page. There you can choose to install stable release or beta. 

Quick Start

  • Connect board to computer via a known good USB and double press the reset button.
  • Download circuitpython UF2 and upload to the FEATHERBOOT drive.
  • Open CIRCUITPY drive and upload the required libraries (listed below) and code.py

Adafruit Circuit Python Libraries

Download the Circuit Python library bundle and unzip the folder. Create a new folder in the CIRCUITPY drive and name it "lib". The following libraries are required to run the code properly. Double check to ensure all of the files and folders are inside the lib folder on the CIRCUITPY drive.

  • adafruit_bus_device (directory)
  • adafruit_lis3dh.mpy
  • neopixel.mpy

Upload code.py

Click the link below to download the project  zip – This contains the code, audio and 3D files. Upload the code.py file to the CIRCUITPY drive.

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.

# SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
Prop-Maker based Key Blade.
Adafruit invests time and resources providing this open source code.
Please support Adafruit and open source hardware by purchasing
products from Adafruit!
Written by Kattni Rembor for Adafruit Industries
Copyright (c) 2019 Adafruit Industries
Licensed under the MIT license.
All text above must be included in any redistribution.
"""

import time
import digitalio
import audioio
import audiocore
import busio
import board
import neopixel
import adafruit_lis3dh

# CUSTOMISE COLORS HERE:
COLOR = (255, 0, 0)  # Default is red
ALT_COLOR = (255, 18, 0)  # Default alternate is orange

# CUSTOMISE IDLE PULSE SPEED HERE: 0 is fast, above 0 slows down
IDLE_PULSE_SPEED = 0.03  # Default is 0.03 seconds

# CUSTOMISE BRIGHTNESS HERE: must be a number between 0 and 1
IDLE_PULSE_BRIGHTNESS = 0.3  # Default maximum idle pulse brightness is 30%
SWING_BRIGHTNESS = 0.4  # Default swing brightness is 40%
HIT_BRIGHTNESS = 1  # Default hit brightness is 100%

# CUSTOMISE SENSITIVITY HERE: smaller numbers = more sensitive to motion
HIT_THRESHOLD = 650
SWING_THRESHOLD = 125

# Set to the length in seconds of the "on.wav" file
POWER_ON_SOUND_DURATION = 1.7

NUM_PIXELS = 40  # Number of pixels used in project
NEOPIXEL_PIN = board.D5
POWER_PIN = board.D10

enable = digitalio.DigitalInOut(POWER_PIN)
enable.direction = digitalio.Direction.OUTPUT
enable.value = False

strip = neopixel.NeoPixel(NEOPIXEL_PIN, NUM_PIXELS, brightness=1, auto_write=False)
strip.fill(0)  # NeoPixels off ASAP on startup
strip.show()

audio = audioio.AudioOut(board.A0)  # Speaker

# Set up accelerometer on I2C bus, 4G range:
i2c = busio.I2C(board.SCL, board.SDA)
accel = adafruit_lis3dh.LIS3DH_I2C(i2c)
accel.range = adafruit_lis3dh.RANGE_4_G

COLOR_HIT = COLOR  # "hit" color is COLOR set above
COLOR_SWING = ALT_COLOR  # "swing" color is ALT_COLOR set above


def play_wav(name, loop=False):
    """
    Play a WAV file in the 'sounds' directory.
    :param name: partial file name string, complete name will be built around
                 this, e.g. passing 'foo' will play file 'sounds/foo.wav'.
    :param loop: if True, sound will repeat indefinitely (until interrupted
                 by another sound).
    """
    print("playing", name)
    try:
        wave_file = open('sounds/' + name + '.wav', 'rb')
        wave = audiocore.WaveFile(wave_file)
        audio.play(wave, loop=loop)
    except:  # pylint: disable=bare-except
        return


def power_on(sound, duration):
    """
    Animate NeoPixels with accompanying sound effect for power on.
    :param sound: sound name (similar format to play_wav() above)
    :param duration: estimated duration of sound, in seconds (>0.0)
    """
    prev = 0
    start_time = time.monotonic()  # Save audio start time
    play_wav(sound)
    while True:
        elapsed = time.monotonic() - start_time  # Time spent playing sound
        if elapsed > duration:  # Past sound duration?
            break  # Stop animating
        animation_time = elapsed / duration  # Animation time, 0.0 to 1.0
        threshold = int(NUM_PIXELS * animation_time + 0.5)
        num = threshold - prev  # Number of pixels to light on this pass
        if num != 0:
            strip[prev:threshold] = [ALT_COLOR] * num
            strip.show()
            prev = threshold


def mix(color_1, color_2, weight_2):
    """
    Blend between two colors with a given ratio.
    :param color_1:  first color, as an (r,g,b) tuple
    :param color_2:  second color, as an (r,g,b) tuple
    :param weight_2: Blend weight (ratio) of second color, 0.0 to 1.0
    :return (r,g,b) tuple, blended color
    """
    if weight_2 < 0.0:
        weight_2 = 0.0
    elif weight_2 > 1.0:
        weight_2 = 1.0
    weight_1 = 1.0 - weight_2
    return (int(color_1[0] * weight_1 + color_2[0] * weight_2),
            int(color_1[1] * weight_1 + color_2[1] * weight_2),
            int(color_1[2] * weight_1 + color_2[2] * weight_2))


mode = 0  # Initial mode = OFF

# Setup idle pulse
min_idle_brightness = 0  # Minimum brightness of idle pulse
max_idle_brightness = IDLE_PULSE_BRIGHTNESS  # Maximum brightness of idle pulse
idle_direction = 1  # Initial idle pulse direction

# Main loop
while True:

    if mode == 0:  # If currently off...
        enable.value = True
        power_on('on', POWER_ON_SOUND_DURATION)  # Power up!
        play_wav('idle', loop=True)  # Play idle sound now
        mode = 1  # Idle mode

        # Setup for idle pulse
        min_idle_brightness = 0
        idle_direction = 0.01
        strip.brightness = min_idle_brightness
        strip.fill(COLOR)
        strip.show()

    elif mode >= 1:  # If not OFF mode...
        x, y, z = accel.acceleration  # Read accelerometer
        accel_total = x * x + z * z
        # (Y axis isn't needed, due to the orientation that the Prop-Maker
        # Wing is mounted.  Also, square root isn't needed, since we're
        # comparing thresholds...use squared values instead.)
        if accel_total > HIT_THRESHOLD:  # Large acceleration = HIT
            TRIGGER_TIME = time.monotonic()  # Save initial time of hit
            play_wav('hit')  # Start playing 'hit' sound
            COLOR_ACTIVE = COLOR_HIT  # Set color to fade from
            strip.brightness = HIT_BRIGHTNESS  # Hit flash brightness
            mode = 3  # HIT mode
        elif mode == 1 and accel_total > SWING_THRESHOLD:  # Mild = SWING
            TRIGGER_TIME = time.monotonic()  # Save initial time of swing
            play_wav('swing')  # Start playing 'swing' sound
            COLOR_ACTIVE = COLOR_SWING  # Set color to fade from
            mode = 2  # SWING mode

        elif mode == 1:
            # Idle pulse
            min_idle_brightness += idle_direction  # Pulse up
            if min_idle_brightness >= max_idle_brightness or min_idle_brightness <= 0:  # Then...
                idle_direction = -idle_direction  # Pulse down
            strip.brightness = min_idle_brightness
            strip.show()
            time.sleep(IDLE_PULSE_SPEED)  # Idle pulse speed set above
        elif mode > 1:  # If in SWING or HIT mode...
            if audio.playing:  # And sound currently playing...
                blend = time.monotonic() - TRIGGER_TIME  # Time since triggered
                if mode == 2:  # If SWING,
                    blend = abs(0.5 - blend) * 2.0  # ramp up, down
                    strip.brightness = SWING_BRIGHTNESS  # Swing brightness
                strip.fill(mix(COLOR_ACTIVE, COLOR, blend))  # Fade from hit/swing to base color
                strip.show()
            else:  # No sound now, but still SWING or HIT modes
                play_wav('idle', loop=True)  # Resume idle sound
                mode = 1  # Return to idle mode

Settings and Customization

If your are building the project as is, you shouldn't have to edit any of the values in the code. But, if you have a different setup, you'll want to read through the comments and make your desired changes.

  • Use the COLOR and ALT_COLOR variables to change the main color of the NeoPixel LEDs.
  • Accelerometer sensitivity can be adjusted in the HIT_THRESHOLD and SWING_THRESHOLD.
  • The number of NeoPixels and data pin can be changed via NUM_PIXELS and NEOPIXEL_PIN.
  • Audio is set to output via pin A0 (That's the analog pin #0 on the Feather M4 express).

Audio Files

There are 4 sound effects used in this project. Each sound can be customized and easily replaced with a different sound – Just need to make sure the format is supported. 

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.

  • Power on – on.wav
  • Idle looping noise – idle.wav
  • Swing whoosh – swing.wav
  • Crash strike – hit.wav

Sound effects were sourced from the freesound.org website.

3D Printed Parts

Parts are designed to be 3D printed with FDM based machines. STL files are oriented to print "as is". Machines with dual extrusion or single extrusion setups are listed below with parts name and description. Parts require tight tolerances that might need adjusting slice setting. Reference the suggested settings below.

CURA Slicing

Parts were sliced using Ultimaker's CURA 4.x software and tested with an Ultimaker S5 and Ultimaker 3. The handle, blade and canister are designed for dual extrusion. Import parts and choose materials per model. Use the Merge Models function to group parts and prepare  slicing for dual extrusion. Rotate to orient the merged models and reposition to center on the build plate. 

Slicing The Handle

The handle and grip are combined to form the handle. Orient the merged model so the flat top of the guard is touching the build plate. 

  • pmkb-handle.stl (Vertigo Galaxy PLA)
  • pmkb-handle-grip.stl (Cool Gray PLA)

Slicing The Blade

The blade is split into two parts, the frame and the teeth. Orient the part so the top of the blade is touching the build plate. Use a large brim (10mm) for best bed adhesion.

  • pmbk-blade-frame.stl (Vertigo Gray PLA)
  • pmbk-blade-teeth.stl (Transparent PLA)

Slicing The Canister

The canister is also two parts, the canister and panels. Orient the part so the top of the canister is touch the build plate – Reference the drafted angle to avoid using support material.

  • pmbk-can.stl (Vertigo Gray PLA)
  • pmbk-can-panels.stl (Transparent PLA)

Slicing The Pipe

The pipe and hose are combined to create one part in two colors. Orient the model so the top flange is touching the build plate.

  • pmkb-pipe-neck.stl (Rapunzel Silver PLA)
  • pmkb-pipe-hoose.stl (Vertigo Galaxy PLA)

Slicing Blade Decals

The blade uses two sets of decals that are dual extruded and glued to the surface of the blade frame – This allows for four different colors in the final part. This should be laid flat on the build plate.

1st Side

  • pmkb-blade-gold-bottom.stl
  • pmkb-blade-silver-bottom.stl

2nd Side

  • pmkb-blade-silver-top.stl
  • pmkb-blade-gold-top.stl

 

3D Parts

Here's the full parts list segmented for better readability.

  • Handle Assembly
    • pmkb-handle.stl
    • pmkb-handle-grip.stl
    • pmkb-handle-decal-a.stl
    • pmkb-handle-decal-b.stl
    • pmkb-handle-end.stl
    • pmkb-ring-single.stl
    • pmkb-ring-double.stl
  • Blade Assembly
    • pmkb-blade-frame.stl
    • pmkb-blade-bcover.stl
    • pmkb-blade-tcover.stl
    • pmkb-blade-teeth.stl
    • pmkb-blade-gold-top.stl
    • pmkb-blade-gold-bottom.stl
    • pmkb-blade-silver-bottom.stl
    • pmkb-blade-silver-top.stl
  • Can Assembly
    • pmkb-can.stl
    • pmkb-can-panels.stl
    • pmkb-can-cover.stl
    • pmkb-pcb-mount.stl
    • pmkb-cap.stl
  • Pipe Assembly
    • pmkb-pipe-neck.stl
    • pmkb-pipe-hoose.stl
    • pmkb-pipe-extender.stl
    • pmkb-tube.stl
    • pmkb-coupler.stl

Filament Colors

List of filament colors used in this project. PLA is by Fillamentum.com these are from their premium filament line of material.

NeoPixel Ring

Two 3-pin JST cables will be wired to the 16x NeoPixel ring. Use heat shrink tubing to keep the wires bundled together – Don't forget to add a pieces of heat shrink onto the cable before soldering! Solder the 3-pin JST male cable to  ground, voltage and data-in pins. Then solder the 3-pin JST female cable to ground, voltage and data-out pins. 

NeoPixel Strip

Cut a segment from the NeoPixel strip containing 28 NeoPixel LEDs – This should be about the length of the blade. Put heat shrink on then Solder the second 3-pin JST cable (male) to the ground, voltage and data-in pads on the NeoPixel strip. Be sure to solder wires to the correct end – The arrow icons on the flex PCB indicate the flow of data. Slip the heat shrink over the joint and heat to seal the connection.

Toggle Switch

Wire up the switch in-line with the ground wire on the JST cable – This will allow the switch to power circuit on and off. Solder ground wires to the middle pin and either far left or right pin on the switch. Don't forget to add heat shrink before soldering!

If you'd like to recharge battery over USB – Connect the switch to the EN and GND pins on the Prop-Maker FeatherWing.
Using a JST adapter with the switch will not allow battery recharging over USB!

Speaker

Remove the existing wires from the speaker. Solder the red wire to the positive marked JST wire and the black to the negative one. 

Feather Header Setup

Install female headers to the Adafruit Feather M4 Express so they're soldered on the top side of the board. Then, solder male headers to the Adafruit PropMaker FeatherWing so they're soldered to the bottom of the PCB. Test the setup by installing the PropMaker on top of the Feather M4 Express.

Canister Setup

Get the pmkb-can-cover and pmkb-can parts and line them up together. Snap fit the cover into the bottom opening of the can. The tolerances are very tight, so I used the surface of the table to force the cover into the can.

3d_printing_can-cover-parts.jpg

3d_printing_can-cover-installing.jpg

3d_printing_can-cover-installed.jpg

Feather Mount

Use four M2.5 x 8mm flat head machine screws to secure the Adafruit Feather M4 to the PCB mount. Place the Feather PCB over the standoffs and line up the mounting holes. Insert and fasten the machine screws to secure. Once fully tightened, snap-on the Prop-Maker FeatherWing so the headers are fully seated.

3d_printing_pcbs-mount-screws.jpg

3d_printing_feather-mount-fasten.jpg

3d_printing_feather-mount-installed.jpg

Install NeoPixel Ring

The 3-pin JST cable from the NeoPixel ring will need to pass through the bottom of the PCB mount. Insert the connector through the side and out the bottom like shown in the photo. This will make installing the components into the can easier. 

3d_printing_pcb-mount-neopixel-wire-install.jpg

3d_printing_pcb-mount-neopixel-wire-installed.jpg

NeoPixel Ring Canister

With the wiring setup, start inserting the NeoPixel ring through the top opening of the can. Carefully push the PCB through the can so it's over the bottom cover. Using your fingers and orient the PCB so the wiring is not tangled up. The NeoPixel PCB is press fitted into the bottom cover. Little nubs on the inner wall will catch the edge of the PCB so it's secured in place with a friction fit.

3d_printing_can-neopixel-installing.jpg

3d_printing_can-neopixel-installed.jpg

3d_printing_can-neopixel-inside.jpg

Connect Switch

Grab the wired toggle switch and begin to plug it in. Connect the 2-pin JST cable into the battery port on the Adafruit Feather. The Feather PCB stack should still be outside the can with the 3-pin JST wire still coming out the bottom.

3d_printing_switch-feather-preplug.jpg

3d_printing_switch-feather-plug.jpg

Connect NeoPixel

Go ahead and grab the 3-pin JST cable (the male one wired to the NeoPixel ring) and plug it into the NeoPixel port on the side of the Prop-Maker FeatherWing.

Speaker Install

Take a moment to install the speaker into the cap. The inner walls of the cap have two little nubs that will catch the outer ring of the speaker metal housing.  Press fit the speaker so it clicks into place. Insert at an angle to get the ring through the first bump.

3d_printing_speaker-cap-preinstall.jpg

3d_printing_speaker-cap-installed.jpg

3d_printing_speaker-feather-plug.jpg

NeoPixel Wire Install

Grab the female 3-pin JST cable and thread it through the opening on the side of the can. Pull it through and begin to work the PCB mount into the can. You'll want to make sure the Feather's USB port is facing towards the circular opening – This will make it easier to access for recharging the battery or reprogramming.

PCB Mount Install

With the PCB mount inside the can, take a moment to orient the components so the wiring is manageable. You'll need to grab the PCB mount and line it up with the two mounting holes. While holding the part in place, insert and fasten two M3 x 6mm to secure the PCB mount to the can. Carefully pull the switch out of the can, through the center of the NeoPixel ring. The switch and JST connector should be protruding from the bottom. Grab the 2200mAh battery and connect it to the JST port on the switch. Flip it on and off to test the circuit. The NeoPixel ring should light up (code should be already uploaded).

3d_printing_can-mount-holes.jpg

3d_printing_can-mount-fasten.jpg

3d_printing_can-neopixel-test.jpg

Blade Details

The blade decals were glued using E6000 adhesive. I used a fine tip (like a syringe) to apply the glue. Allow the glue to set and dry before proceeding with the assembly. When it's ready, get the wired NeoPixel strip.

3d_printing_blade-details.jpg

3d_printing_blade-details-installed.jpg

3d_printing_neopixel-strip-blade-preinstall.jpg

NeoPixel Blade Installation

Insert the 3-pin JST cable through the bottom opening of the blade. Push the NeoPixel strip through and get the JST cable to come out of the side opening. I used a pair tweezers to grab hold of the connect and pulled it out the side. I used a spudger tool to line the NeoPixel ring with the back wall of the blade – The further away the light source is from serrated teeth, more diffused the effect will look. The tolerances are fairly tight inside the blade, so it has a friction hold. Optionally could use double-sided tape to secure the strip. Test the blade by plugging in the male JST cable to the female one from the NeoPixel ring.

3d_printing_neopixel-wire-insert-blade.jpg

3d_printing_neopixel-wire-install-blade.jpg

3d_printing_blade-circuit-test.jpg

Install Speaker Cap

Now is a good time to close the top of the can. Grab the cap part with the speaker installed and press fit it over the top opening. This has a twist locking mechanism. Line up the nubs so the speaker cap press fits and twist to lock it in place.

Install Blade

Grab the blade with one hand and the can with the other. Position the blade and the can so they're in the correct orientation. In order for the blade to snap fit, you'll need to apply pressure over the Feather PCB. I suggest temporarily removing the Prop-Maker FeatherWing and using your two index fingers to snap the blade into the can. Start with one side first then get the second to click into place. Use your thumbs to keep the two parts together, it'll help stabilize whole build. Be careful handling the blade and do not grab hold of the serrated teeth – they can hurt you so please be very careful!

3d_printing_blade-can-preinstall.jpg

3d_printing_blade-can-installing.jpg

NeoPixel Blade Power Test

With the blade installed, it's a good idea to test the circuit again to check all the components are still working. Now is a good time to take a moment to organize the wiring or reduce any of the wire lengths.

Pipe Installation

Get the pmkb-coupler and pmkb-pipe-neck parts together. Use three M3 x 8mm pan head machine screws to secure the coupler to the pipe. Place the coupler over the flange on the bottom of the pipe. Line up the mounting holes. Start by fastening the top screw through the coupler and hose.

3d_printing_pipe-cover-screws.jpg

3d_printing_pipe-hose-screw.jpg

3d_printing_pipe-cover-fastening.jpg

Battery Install

Grab the blade assembly and place the pipe next to it. Position the two assemblies so they're in the correct orientation. The inner diameter of the piping is designed to snuggly fit the 2200mAh battery. Begin to insert the 2200mAh battery through the piping and pull it out to test the tolerances. If it's effortless to get in and out, you can safely push it down all the way through.

3d_printing_blade-can-pipe.jpg

3d_printing_battery-pipe-installed.jpg

Install Toggle Switch

The toggle switch is panel mounted to the side of the coupler – There's a opening on the side thats sized to fit the threaded stem on the switch. Use the included  hardware to secure the toggle switch to the coupler. Fasten the hex nut using pair of vise grips so it's tightly secured to the side of the coupler.

Wire Management

Now it's time to get the two main assemblies. The top of the couple snap fits and locks onto the bottom cover of the can. Before the two can be connected, the wiring needs to be fitted inside the assembly. There should be room in the couple and down the pipe for any access wires.

Insert the coupler at an angle so the semicircle is seated first, then work the next of the tabs through the bottom cover. Apply firm pressure to the parts to snap fit them together.

3d_printing_pipe-can-preinstall.jpg

3d_printing_blade-assembly-test.jpg

Install Tubing & Handle

The handle screws onto the pmkb-tube part. These have screw-threaded ends that twist together. The pmkb-pipe-extender part is next, twist that onto the pmkb-tube part. Lastly, screw the pipe extender into the end of the pipe with the blade.

This guide was first published on Apr 10, 2019. It was last updated on Mar 28, 2024.