TRON DISC
2023-08-15 | By Adafruit Industries
License: See Original Project 3D Printing LED Strips Programmers Wearables Adafruit Feather
Courtesy of Adafruit
Guide by Ruiz Brothers
Overview
You can build a Tron-inspired prop using NeoPixel LEDs and CircuitPython.
Powered by the Adafruit Feather and Prop-Maker FeatherWing, this Tron disc is fully 3D printed.
With the power of neodymium magnets, this disc can actually attach to your back!
Using a mobile device and the Bluefruit app, you can change the color of the LEDs, so it's easy to switch your team color.
The Prop-Maker FeatherWing and Feather Bluefruit are the perfect pair for making advanced props with motion-activated lights and sounds that can be controlled wirelessly from your phone or watch.
The code is written in CircuitPython, so it’s easy to modify to fit just about any prop with lights and sounds.
It uses Bluetooth (BLE) and the LED animation library to trigger lighting effects with the Prop-Maker’s on-board accelerometer.
Parts
1 x Running Vest
8 x Magnets
1 x Slide Switch
1 x JST PH 2mm 3-Pin
4 x M2x10mm Screws
CircuitPython for Feather nRF52840
CircuitPython is a derivative of MicroPython designed to simplify experimentation and education on low-costmicrocontrollers. 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.
The following instructions will show you how to install CircuitPython. If you've already installed CircuitPython but are looking to update it or reinstall it, the same steps work for that as well!
Set up CircuitPython Quick Start!
Follow this quick step-by-step for super-fast Python power :)
Download the latest version of CircuitPython for this board via CircuitPython.org
Click the link above to download the latest UF2 file.
Download and save it to your desktop (or wherever is handy).
Plug your Feather nRF52840 into your computer using a known-good USB cable.
A lot of people end up using charge-only USB cables and it is very frustrating! So, make sure you have a USB cable you know is good for data sync.
Double-click the Reset button next to the USB connector on your board, and you will see the NeoPixel RGB LED turn green (identified by the arrow in the image). If it turns red, check the USB cable, try another USB port, etc. Note: The little red LED next to the USB connector will pulse red. That's ok!
If double-clicking doesn't work the first time, try again. Sometimes it can take a few tries to get the rhythm right!
You will see a new disk drive appear called FTHR840BOOT.
Drag the adafruit_circuitpython_etc.uf2 file to FTHR840BOOT.
The LED will flash. Then, the FTHR840BOOT drive will disappear, and a new disk drive called CIRCUITPY will appear.
That's it, you're done! :)
Code
Once you've set up your Feather nRF52840 with CircuitPython, you can access the code and necessary libraries bydownloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download to your computer as a zipped folder.
# SPDX-FileCopyrightText: 2021 Liz Clark for Adafruit Industries # # SPDX-License-Identifier: MIT """ Prop-Maker based Tron Disk Adapted from the Darksaber code Adafruit invests time and resources providing this open source code. Please support Adafruit and open source hardware by purchasing products from Adafruit! Written by Liz Clark for Adafruit Industries Copyright (c) 2023 Adafruit Industries Licensed under the MIT license. All text above must be included in any redistribution. """ import time import random import board from digitalio import DigitalInOut, Direction import neopixel import adafruit_lis3dh from adafruit_led_animation.animation.solid import Solid from adafruit_led_animation.animation.pulse import Pulse from adafruit_led_animation.animation.comet import Comet from adafruit_bluefruit_connect.packet import Packet from adafruit_bluefruit_connect.color_packet import ColorPacket from adafruit_ble import BLERadio from adafruit_ble.advertising.standard import ProvideServicesAdvertisement from adafruit_ble.services.nordic import UARTService # BLE setup ble = BLERadio() uart_service = UARTService() advertisement = ProvideServicesAdvertisement(uart_service) # CUSTOMISE SENSITIVITY HERE: smaller numbers = more sensitive to motion HIT_THRESHOLD = 250 SWING_THRESHOLD = 150 # Set to the length in seconds of the "on.wav" file POWER_ON_SOUND_DURATION = 1.7 # NeoPixel setup NUM_PIXELS = 37 # Number of pixels used in project NEOPIXEL_PIN = board.D5 POWER_PIN = board.D10 enable = DigitalInOut(POWER_PIN) enable.direction = Direction.OUTPUT enable.value = False strip = neopixel.NeoPixel(NEOPIXEL_PIN, NUM_PIXELS, brightness=.5, auto_write=False) strip.fill(0) # NeoPixels off ASAP on startup strip.show() # default NeoPixel color is white COLOR = (0, 161, 255) # NeoPixel animations pulse = Pulse(strip, speed=0.05, color=COLOR, period=3) solid = Solid(strip, color=COLOR) comet = Comet(strip, speed=0.05, color=COLOR, tail_length=40) #audio try: from audiocore import WaveFile except ImportError: from audioio import WaveFile try: from audioio import AudioOut except ImportError: try: from audiopwmio import PWMAudioOut as AudioOut except ImportError: pass # not always supported by every board! audio = AudioOut(board.A0) # Speaker wave_file = None # Set up accelerometer on I2C bus, 4G range: i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller accel = adafruit_lis3dh.LIS3DH_I2C(i2c) accel.range = adafruit_lis3dh.RANGE_4_G 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). """ global wave_file # pylint: disable=global-statement print("playing", name) if wave_file: wave_file.close() try: wave_file = open('sounds/' + name + '.wav', 'rb') wave = WaveFile(wave_file) audio.play(wave, loop=loop) except OSError: pass # we'll just skip playing then 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) """ 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 comet.animate() # List of swing wav files without the .wav in the name for use with play_wav() swing_sounds = [ 'swing1', 'swing2', 'swing3', 'swing4', ] # List of hit wav files without the .wav in the name for use with play_wav() hit_sounds = [ 'hit1', 'hit2', 'hit3', 'hit4', ] mode = 0 # Initial mode = OFF #RGB LED red_led = DigitalInOut(board.D11) green_led = DigitalInOut(board.D12) blue_led = DigitalInOut(board.D13) red_led.direction = Direction.OUTPUT green_led.direction = Direction.OUTPUT blue_led.direction = Direction.OUTPUT blue_led.value = True red_led.value = True green_led.value = True # Darksaber start-up before loop 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 while True: # begin advertising BLE ble.start_advertising(advertisement) # if no BLE connection... # allows it to be used without the bluefruit app connection while not ble.connected: if 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(random.choice(hit_sounds)) # Start playing 'hit' sound # NeoPixels are solid on with a hit solid.animate() 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(random.choice(swing_sounds)) # Randomly choose from available swing sounds while audio.playing: pass # wait till we're done mode = 2 # we'll go back to idle mode elif mode == 1: # pulse animation when idling or swinging pulse.animate() 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 else: # No sound now, but still SWING or HIT modes play_wav('idle', loop=True) # Resume idle sound mode = 1 # Return to idle mode ble.stop_advertising() # if BLE is connected... while ble.connected: # color picker from bluefruit app if uart_service.in_waiting: packet = Packet.from_stream(uart_service) # if a color packet is recieved... if isinstance(packet, ColorPacket): print(packet.color) # color for the different animations are updated comet.color = packet.color solid.color = packet.color pulse.color = packet.color solid.animate() # repeat of the above code if 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(random.choice(hit_sounds)) # Start playing 'hit' sound # NeoPixels are solid on with a hit solid.animate() 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(random.choice(swing_sounds)) # Randomly choose from available swing sounds while audio.playing: pass # wait till we're done mode = 2 # we'll go back to idle mode elif mode == 1: # pulse animation when idling or swinging pulse.animate() 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 else: # No sound now, but still SWING or HIT modes play_wav('idle', loop=True) # Resume idle sound mode = 1 # Return to idle mode
Upload the Code and Libraries to the Feather nRF52840
After downloading the Project Bundle, plug your 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 Feather nRF52840's CIRCUITPY drive.
code.py
lib directory
sound directory
Your Feather nRF52840 CIRCUITPY drive should look like this after copying the lib folder, sound folder and the code.py file.
Circuit Diagram
The wiring diagram below provides a visual reference for connecting the components. It is not true to scale; it is justmeant 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 wiredconnections - the length of wire, position and size of components are not exact.
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
Filament Change
Use the filament change script to print the disc in multiple colors.
Go to Extensions -> Post Processing -> Modify G-Code
Select Filament Change.
Top Cover Layers
Enter 12 in the Layer section to print the first 12 layers in white PLA.
Click on add script and select Filament Change to add one more color change.
Layers 13 to 123 will print in black PLA.
Enter 123 in the Layer section to print the rest of the cover in white PLA.
Layers 124 and up will print the remainder in white PLA.
Bottom Cover Layers
Add a filament change script and enter 3 in the Layer section.
Layers 1 to 3 will print in black PLA. Layers 4 and up will print in white PLA.
Assembly
Solder Headers
Align socket header pins to the Prop-Maker wing. Use a breadboard to help solder the short side of the headers to the Prop-Maker.
Solder socket headers to the top side of the Feather nRF52840 board. Then, solder pin headers to the bottom side of the Prop-Maker FeatherWing so it can be plugged into the Feather.
Mount Feather
Align the Feather board to the stand-offs on the bottom cover part, with the USB port facing the slide switch mount.
Measure LED strip
Place the LED strips over the inner and outer walls, measure and cut to length.
The side light LEDs will mount to the inner wall.
Remove Sheath
Remove the sheath to fit the LED strips around the walls.
LED wires
Use a 3 pin JST connector to connect the LED strips to the Prop-Maker port.
Solder LED strips
Measure a short silicone ribbon to connect both LED strips.
Place LEDs
Plug the 3 pin JST connector into the STEMMA port on the Prop-Maker.
USB extension
Measure a short 5 wire silicon ribbon cable to extend the USB port.
Mount the USB breakout
The USB breakout board mounts with two M2.5x4mm long screws.
Speaker Mount
Connect the speaker to the port on the Prop-Maker.
Slide switch mount
Measure a short 2 wire cable to enable the slide switch.
EN and G on the Prop-Maker connect to any two pins on the slide switch.
The slide switch press fits at an angle with the slide positioned in the middle.
Battery extension
Shorten a 2 pin JST wire to connect the battery.
Battery mount
A 2200mAh battery slides between the clips on the base.
Place Magnets
Carefully orient the magnets in each mount. The caps tightly press fit over the magnets.
Attach cover
Align the slide switch and USB port to the top cover.
Cover screws
Align the case to the four screws holes on the side.
Use four M2x10mm long nylon screws to attach the top and bottom cover together.
Harness assemble
The harness Plate is attached to the back of a Running Vest with nylon string. Pass the string through the mesh fabric and loops on the Plate.
Place magnets into each mount and press-fit a printed cap over each one.
Complete!
Use
Bluetooth Control
This project uses the Adafruit Bluefruit LE connect app (available free for Android and iOS) to trigger the lights and sounds. It uses the Color Picker module to choose different colors. If you haven't downloaded the app yet, use the button below to install it on your mobile device.
Download BLE Connect App for Android
Using Bluefruit LE Connect App
Turn on the Feather by either connecting it via USB to your computer or with the slide switch.
Open the Bluefruit LE Connect app and locate the device named CIRCUITPY and tap the connect button. Locate and tap on Controller. Under module, tap on Color Picker.
Color Picker
You can change the color of the NeoPixel LEDs by using the Color Picker. Tap connect, select Controller. Under module, tap on Color Picker. Use the color wheel to select a color and the slider to adjust the brightness. Tap the send color button to trigger the color.
Apple Watch
If you have an Apple Watch, you can use the Bluefruit LE Connect App just like the mobile app. The Apple Watch app is included with the iOS app download.
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum