INTRODUCTION
Hey all!
I wanted to share my wizard crystal scepter / staff build, aimed towards renaissance fairs or Halloween costumes.
The majority of this prop is built using 1" PVC Pipe, found at any big box hardware store such as Lowes or Home Depot. There are a few 3D printed components.
Some experience with coding is recommended.
REQUIRED 3D PRINTED PARTS
https://makerworld.com/en/models/2161070-wizard-crystal-scepter-staff#profileId-2343062
REQUIRED COMPONENTS
1x SPDT Slide Switch https://www.adafruit.com/product/805
1x Button Switch https://www.adafruit.com/product/367
1x RP2040 PropMaker board https://www.adafruit.com/product/5768
1x Lithium Ion 3.7v battery https://www.adafruit.com/product/1781
3 conductor wire (at least 6-7 feet in length)
https://www.amazon.com/dp/B0DMK8HK6R?ref=ppx_yo2ov_dt_b_fed_asin_title&th=1 (or similar)
1x 5ft 1" PVC Pipe https://www.lowes.com/pd/Charlotte-Pipe-1-in-dia-x-5-ft-L-450-PSI-PVC-Pipe/3133093 (Or similar)
1x 1" PVC Pipe Cap https://www.homedepot.com/p/Charlotte-Pipe-1-in-PVC-Socket-Schedule-40-Pressure-Cap-PVC021161000HD/203818100?source=shoppingads&locale=en-US&fp=ggl (or similar)
EVA Foam. I used 5mm thickness.
https://www.amazon.com/dp/B08YXXCG6J?ref=ppx_yo2ov_dt_b_fed_asin_title&th=1
EVA Clay foam (Optional but recommended)
https://www.michaels.com/product/modeling-foam-by-make-market-10625195
Rubber Cement https://www.walmart.com/ip/Gorilla-4-Ounce-Crystal-Clear-Rubber-Cement-Bottle-with-Brush-Applicator-Model-105779-1-Pack/105406946?wl13=1885&selectedSellerId=0&wmlspartner=wlpa (or similar)
1x Can of Plastidip https://www.lowes.com/pd/Plasti-Dip-11-fl-oz-Gray-Aerosol-Spray-Rubberized-Coating/50107784
BUILD INSTRUCTIONS
3D printed parts
- Starting with the LED 3D printed parts, insert the square bar into the cutout in the circle until the end is flush with the face of the circle. glue these parts together and let the glue cure.
- Once the collar is either printed in a color that you like or is painted and ready for use, glue the crystal in place on top of it. It can only be placed in the collar one way.
- Same as the collar, print the button holder in a filament you like, or paint it before assembly.
START (PVC PIPE)
The length of the PVC pipe will determine how long the scepter / staff is, plus the additional height of the crystal and collar. I left my PVC pipe at 5ft.
Use the heat gun to slowly add curves to the PVC pipe to give it a more natural look. Do not curve the pipe too much, as it may kink the pipe.
Once you are happy with the shape, lay out the EVA foam and see how much you will need to cover the pipe. Make sure to leave a portion of the both ends uncovered so that the cap and crystal / collar will still fit over the pipe.
Use the rubber cement on the PVC pipe and EVA foam. glue the EVA foam to the pipe.
If you are trying to add a wooden texture to the handle, use either a box cutter or a dremel. If you use a box cutter, simple curved slices + the heat gun will open up the cuts and give it a more textured look.
If there are gaps in the EVA foam, use EVA clay foam and sandpaper to hide them.
PAINTING THE STAFF
Once you are happy with the texture of the staff, prep it for paint by hanging it in a place with good ventilation.
Cover the entirety of the staff (minus the exposed PVC pipe ends) with plastidip. Multiple layers are recommended. This will make the finish more durable and much easier to paint.
There are various paints that are acceptable for the staff. I would not recommend water based acrylic due to your hand potentially rubbing it off with sweat.
WIRING
Once the 3D printed LED parts are glued together, solder the 3 wires onto the LED and glue the LED strip to the square bar that was glued in place. Cut to length.
Glue the LED circle / bar assembly to the top of the staff on the inside of the PVC Pipe
Note: I used 19 LEDs. You can use more or less, but the value will need to be changed in the code. it is under section RP2040 BOARD NEOPIXEL POWER, line 53.
Find a location suitable for the button holder printed earlier, and cut out the EVA foam in the way. Then drill out two holes and dremel away the remaining PVC pipe so that the button holder sits flush against the foam.
Do not glue in place yet!
Solder the wires onto the buttons as shown (do not solder anything to the RP2040 yet)
glue the buttons into their respective slots in the button holder
Run the button wires and LED wires down the length of the PVC pipe, ideally about 6-12" of extra wire at the bottom.
Solder the RP2040 PropMaker as shown in the diagram below. Tape in place on the battery / pi holder printed earlier. Place the battery in the holder with the Pi.
ASSEMBLY
Slide the collar / crystal assembly over top the LEDs
Insert the battery / Pi holder assembly into the bottom of the staff, into the PVC.
Note: It is a VERY tight fit. you may need to push the PropMaker over to clear the battery connector.
CODE
After installing Circuit Python on the RP2040 Propmaker, copy and paste this code into the code.py file. I use Mu Editor as it is what Adafruit recommends.
Some libraries will need to be installed. Read through the IMPORTING LIBRARIES section of the code to see what you will need.
https://learn.adafruit.com/debouncer-library-python-circuitpython-buttons-sensors/basic-debouncing
https://docs.circuitpython.org/projects/simpleio/en/latest/
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
************************************************************
IMPORTING LIBRARIES
************************************************************
"""
import os
import board
import neopixel
import time
from adafruit_debouncer import Button
from digitalio import DigitalInOut, Direction, Pull
import simpleio
#Import all Adafruit LED Animations
from adafruit_led_animation.helper import PixelSubset
from adafruit_led_animation.animation.solid import Solid
from adafruit_led_animation.animation.blink import Blink
from adafruit_led_animation.animation.comet import Comet
from adafruit_led_animation.animation.rainbowcomet import RainbowComet
from adafruit_led_animation.animation.rainbow import Rainbow
from adafruit_led_animation.animation.sparklepulse import SparklePulse
from adafruit_led_animation.animation.pulse import Pulse
from adafruit_led_animation.color import PURPLE, AMBER, JADE, RED, CYAN, BLUE, TEAL, ORANGE, YELLOW, AQUA, GREEN, MAGENTA, PINK, GOLD, WHITE
"""
************************************************************
ESTABLISH COLORS
************************************************************
"""
#Import Adafruit predetermined colors
COLORS = [RED, YELLOW, AMBER, ORANGE, GREEN, JADE, TEAL, AQUA, CYAN, BLUE, PURPLE, MAGENTA, PINK, GOLD, WHITE]
G1_COLOR = 0
G2_COLOR = 1
"""
************************************************************
RP2040 BOARD NEOPIXEL POWER
************************************************************
"""
# 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
# Set pin for external neopixels
pixel_pin = board.EXTERNAL_NEOPIXELS
# Set number of pixels on the strip
pixel_num = 19
pixel_brightness = 0.5
pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=pixel_brightness, auto_write=True)
# external button
pin = DigitalInOut(board.EXTERNAL_BUTTON)
pin.direction = Direction.INPUT
pin.pull = Pull.UP
switch = Button(pin, long_duration_ms = 1000)
switch_state = False
"""
************************************************************
LED GROUPS
************************************************************
"""
G1 = PixelSubset(pixels, 0, 9)
G2 = PixelSubset(pixels, 9, 19)
"""
**********************************************************************************************************************
PROGRAM START
**********************************************************************************************************************
"""
MODE = 0
STARTUP = 0
while True:
# Initialize all animations once in the beginning
if STARTUP == 0:
solid = Solid(pixels, color=COLORS[G1_COLOR])
Comet1 = Comet(G1, speed=0.05, color=COLORS[G1_COLOR], tail_length=18, bounce=True)
Comet2 = Comet(G2, speed=0.07, color=COLORS[G2_COLOR], tail_length=18, bounce=True)
sparkle = SparklePulse(pixels, speed=0.08, period = 0.02, color=COLORS[G1_COLOR])
pulse = Pulse(pixels, speed=0.01, color=COLORS[G1_COLOR], period=3)
Dpulse1 = Pulse(G1, speed=0.01, color=COLORS[G1_COLOR], period=3.2)
Dpulse2 = Pulse(G2, speed=0.01, color=COLORS[G2_COLOR], period=5.4)
rainbow = Rainbow(pixels, speed=0.1, period=2)
rainbow_comet = RainbowComet(pixels, speed=0.08, tail_length=30, bounce=True)
STARTUP = 1
switch.update()
# -----------SOLID MODE-----------------
if MODE == 0:
print(MODE)
solid.animate()
if switch.short_count == 1:
MODE = 1
elif switch.long_press:
MODE = 10
# -----------COMET MODE--------------------
elif MODE == 1:
print(STARTUP)
Comet1.animate()
Comet2.animate()
#Switch to next mode
if switch.short_count == 1:
MODE = 2
elif switch.long_press:
MODE = 10
# -----------SPARKLE MODE-----------------
elif MODE == 2:
print(MODE)
sparkle.animate()
if switch.short_count == 1:
MODE = 3
elif switch.long_press:
MODE = 10
# -----------PULSE MODE-------------------
elif MODE == 3:
print(MODE)
pulse.animate()
if switch.short_count == 1:
MODE = 4
elif switch.long_press:
MODE = 10
# -----------DUAL PULSE MODE--------------
elif MODE == 4:
print(MODE)
Dpulse1.animate()
Dpulse2.animate()
if switch.short_count == 1:
MODE = 5
elif switch.long_press:
MODE = 10
# -----------RAINBOW MODE-----------------
elif MODE == 5:
print(MODE)
rainbow.animate()
if switch.short_count == 1:
MODE = 6
elif switch.long_press:
MODE = 10
# -----------RAINBOW COMET MODE-----------
elif MODE == 6:
print(MODE)
rainbow_comet.animate()
if switch.short_count == 1:
MODE = 0
elif switch.long_press:
MODE = 10
#Switch to Color Changing Mode
elif MODE == 10:
#print(MODE)
if switch.short_count == 1:
G1_COLOR = (G1_COLOR + 1) % 15
G1.fill(COLORS[G1_COLOR])
pixels.show()
print(G1_COLOR)
STARTUP = 0
elif switch.short_count == 2:
MODE = 11
elif switch.long_press:
pixels.show()
MODE = 0
elif MODE == 11:
if switch.short_count == 1:
G2_COLOR = (G2_COLOR + 1) % 15
G2.fill(COLORS[G2_COLOR])
pixels.show()
print(G1_COLOR)
STARTUP = 0
elif switch.short_count == 2:
MODE = 10
elif switch.long_press:
pixels.show()
MODE = 0
COMPLETED BUILD / FUNCTIONALITY
Once everything is wired together, the staff should have the following functionalities
- SPDT switch turns the staff on and off.
- Tactile button has multiple functions
- single short press will cycle between the 6 different modes
- single long press will switch to color selection mode for the primary color.
- While in color selection mode, double pressing the tactile button will switch to color selection mode for the secondary color.
- single long press while in color selection mode will return to the 6 different modes, starting from the beginning.
Thank you for taking the time to check this out, and if you built it, even better!
This page (Wizard Crystal Scepter / Staff) was last updated on December 27, 2025.