The Motion-Triggered Lightblast Top Hat

by trymbf in Circuits > Arduino

150 Views, 1 Favorites, 0 Comments

The Motion-Triggered Lightblast Top Hat

20250721_235346(1) (1).jpg
20250721_235355(1).gif
20250721_235543(1).gif

So my cousin and I were thinking of ideas on what we could design together when we got the idea of a formal hat that transforms into a party hat. The perfect mix of fun and fancy!


Meet The Motion-Triggered Lightblast Top Hat - a sophisticated top hat that knows how to party. By day, it's the picture of elegance and class. But when you move, it springs to life with vibrant LED lights. Whether you're walking down the street, dancing at a party, or simply nodding your head, this hat responds instantly, transforming you from refined gentleman to the life of the party in seconds.


This instructable shows you how to built it from start to finish. We'll go through the 3D modeling in Fusion, printing the parts, wiring up the electronics, and getting everything to work together. By the end, you'll have a unique wearable that's guaranteed to turn heads and start conversations wherever you go.

Supplies

20250720_132229.jpg
20250720_132249.jpg
20250720_132259.jpg
20250720_132207 (1).jpg
20250720_132240.jpg
20250720_132320.jpg
20250720_161709.jpg

For the electronics, you will need:

  1. Arduino UNO Rev 3 (or similar microcontroller)
  2. Breadboard
  3. WS2812B LED strip (cut to size)
  4. GY-521 (or similar MPU6050 based IMU)
  5. 220 ohm resistor
  6. Male to male jumper wires
  7. If you want to go without a computer/power bank attached:
  8. 9V Battery (buy at a retailer near you)
  9. 9V Battery to barrel jack or 9V Battery to pin male jumper wire (solder yourself)

To communicate with the Arduino from your computer, you will need:

  1. USB-A to USB-B cable

To build the hat, you will need:

  1. PLA filament in color of your choosing
  2. 13 M3x10 screws and bolts
  3. Either:
  4. Double-sided tape (buy at a retailer near you)
  5. Hot glue

Insert Resistor Into Breadboard

20250720_142418.jpg
20250720_142428.jpg

We start with connecting the Arduino to the LED strip. To protect the LED strip from voltage drops and signal noise, we need a resistor on the signal line. First, insert the 220-ohm resistor into the breadboard with one foot in each half of the board. Make sure that the feet go all the way in.

Connect the LED Signal Wires

20250720_142624.jpg
20250720_142650.jpg
20250720_142728.jpg

Now connect one jumper wire to the same row as the left leg of the resistor, and another jumper wire to the same row as the right leg. This will connect the two wires together through the resistor. To learn more about how break boards are connected, see here.

Connect the Left Cabel to the Arduino

20250720_145213 (1).jpg
20250720_145217.jpg

Connect the left cable to the Arduino's digital pin 6. The Arduino will use this pin to communicate with the LED strip.

Connect Signal to LED Strip

20250720_145512.jpg
20250720_145557.jpg

Connect the right cable to your LED strips data cable. in my case, it is the yellow one. Now we have connected Arduino's digital pin 6 to our led strip's data cable through a 220-ohm resistor.

Connect Power and Ground to LED Strip

20250720_150647.jpg
20250720_150711.jpg
20250720_150958.jpg

Now connect the black cable in the LED strip to one of the ports marked "GND" on the Arduino. Then connect the red cable to the port marked 5V on the Arduino. This will power the LED strip.

Insert the GY-521

20250720_151633.jpg
20250720_151629.jpg

We need an IMU, inertial measurement unit, to detect movement. For this project, we have chosen the GY-521. Insert the GY-521 into the breadboard as shown in the picture.

Connecting Power and Ground to GY-521

20250720_152222.jpg
20250720_152214.jpg
20250720_152206.jpg

To connect the GY-521, we use the connect rows of the breadboard. Just like we did in step 2. First, connect the GY-521's VCC pin to the Arduino's 3.3V pin. Then connect the GY-521's GND pin to one of the Arduino's GND pins. The pins should be marked on top of the GY-521. If you are unsure, double-check with the images above.

Connecting Data to GY-521

20250720_153106.jpg
20250720_153101.jpg
20250720_153052.jpg
20250720_153039.jpg

The GY-521 communicates to the Arduino via a protocol called I2C; because of this, it needs two data wires (SDA and SCL). Connect the pin marked SCL on the GY-521 to the Arduino's analogue pin A5. Then connect the pin marked SDA on the GY-521 to the Arduino's analogue pin A4.

Take a Small Break and Double Check Wiring

Congrats! You’re done with the wiring 🎉, but it’s always wise to double-check. Take a short break, then come back and review the wiring using the diagram below.


MPU6050 | Arduino

-------- | --------

VCC --> 3.3V

GND --> GND

SCL --> A5 (Uno)

SDA --> A4 (Uno)


WS2812B LED Strip | Breadboard | Arduino

------------------- | ------------- | --------

+5V (Red) --> 5V

GND (Black) --> GND

DIN (White) --> 220 ohm resistor --> D6 (Uno)

Preparing IDE

Skjermbilde 2025-07-20 154211.png
Hvordan instalere bmi 1.png
Hvordan instalere NeoPixel 1.png

First, install the Arduino IDE here. We will use Arduino IDE to send code to our microcontroller, the Arduino UNO.


To communicate with our two components, we need some libraries. Install the following using Arduino IDE's built-in library manager:

  1. MPU6050 library by Electronics Cats - To communicate with GY-521
  2. Adafruit Neopixel library by Adafruit - To control the LED strip

For how to install libraries, see the images above or this article.

Coding

Skjermbilde 2025-07-20 154831.png

Now we are ready to code. After installing the libraries your screen should look something like the image above. Normally, you would need to write your own code, but for this one we have all you need. Replace the code in Arduino IDE with the code below.


(You can also download the code in the file below this step)

#include <Wire.h>
#include <MPU6050.h>
#include <Adafruit_NeoPixel.h>

// ----------------- USER SETTINGS -----------------

// Choose which LED effect to run on jump detection:
// 0 = Colorful Blink (flashes red, green, blue colors)
// 1 = Rainbow Wave (smooth rainbow animation across strip)
const int SELECTED_EFFECT = 0;

// Jump detection parameters:
// Lower threshold = more sensitive jump detection (may cause false triggers)
const float ACCEL_THRESHOLD = 400.0; // Acceleration change threshold to detect a jump
const unsigned long COOLDOWN_MS = 500; // Minimum delay between detections in milliseconds

// LED strip configuration
#define LED_PIN 6 // Pin connected to NeoPixel data input
#define LED_COUNT 30 // Number of LEDs in the strip

// Colorful blink effect settings
const int BLINK_TIMES = 3; // How many times to blink on jump
const unsigned long BLINK_ON_DURATION_MS = 50; // LED ON time per color in ms
const unsigned long BLINK_OFF_DURATION_MS = 25; // LED OFF time between colors in ms

// Rainbow wave effect settings
const int RAINBOW_CYCLES = 5; // Number of full rainbow cycles to animate
const unsigned long RAINBOW_STEP_DELAY_MS = 20; // Delay between animation frames in ms

// -------------------------------------------------

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
MPU6050 mpu;

// For jump detection timing and filtering
unsigned long lastTriggerTime = 0;
float filteredDiff = 0;
const float alpha = 0.1; // EMA smoothing factor for acceleration changes

// Baseline acceleration values (measured at startup)
int16_t baselineX, baselineY, baselineZ;
float baselineMagnitude = 0;

// Animation state tracking
bool isEffectRunning = false;
unsigned long effectTimer = 0;

// --- EFFECT MANAGEMENT STRUCTURE ---

// Function pointer types for animation control functions
typedef bool (*EffectStepFunc)(unsigned long now);

struct Effect {
const char* name; // Name shown in serial output
EffectStepFunc stepFunc; // Called repeatedly until effect is done
void (*startFunc)(); // Called once to initialize effect
};

// Forward declarations of effect functions
void startColorfulBlink();
bool stepColorfulBlink(unsigned long now);

void startRainbowWave();
bool stepRainbowWave(unsigned long now);

// Available effects
Effect EFFECTS[] = {
{"Colorful Blink", stepColorfulBlink, startColorfulBlink},
{"Rainbow Wave", stepRainbowWave, startRainbowWave},
};

const int NUM_EFFECTS = sizeof(EFFECTS) / sizeof(EFFECTS[0]);
int currentEffectIndex = -1;

// ------------------------------------
// Utility: Calculate vector magnitude
float magnitude(int16_t x, int16_t y, int16_t z) {
return sqrt((long)x * x + (long)y * y + (long)z * z);
}

// Measure baseline acceleration (device must be still during calibration)
void calibrateBaseline() {
long sumX = 0, sumY = 0, sumZ = 0;
const int samples = 50;

Serial.println("Calibrating baseline... Keep device still!");

for (int i = 0; i < samples; i++) {
int16_t ax, ay, az;
mpu.getAcceleration(&ax, &ay, &az);
sumX += ax;
sumY += ay;
sumZ += az;
delay(20);
}

baselineX = sumX / samples;
baselineY = sumY / samples;
baselineZ = sumZ / samples;
baselineMagnitude = magnitude(baselineX, baselineY, baselineZ);

Serial.print("Baseline magnitude: ");
Serial.println(baselineMagnitude);
}

void setup() {
Serial.begin(115200);
Wire.begin();

strip.begin();
strip.show(); // Initialize all pixels to off
strip.setBrightness(50);

mpu.initialize();
if (!mpu.testConnection()) {
Serial.println("MPU6050 connection failed! Check wiring.");
while (1);
}

Serial.println("MPU6050 ready!");
calibrateBaseline();

// Validate user effect selection
if (SELECTED_EFFECT < 0 || SELECTED_EFFECT >= NUM_EFFECTS) {
Serial.println("Invalid SELECTED_EFFECT, defaulting to 0");
currentEffectIndex = 0;
} else {
currentEffectIndex = SELECTED_EFFECT;
}
}

void loop() {
unsigned long now = millis();

// Read raw acceleration data
int16_t ax, ay, az;
mpu.getAcceleration(&ax, &ay, &az);

// Calculate magnitude and difference from baseline
long magSquared = (long)ax * ax + (long)ay * ay + (long)az * az;
if (magSquared < 0) magSquared = 0; // Safety check

float currentMag = sqrt(magSquared);
float diff = abs(currentMag - baselineMagnitude);

// Apply exponential moving average to smooth out noise
filteredDiff = alpha * diff + (1 - alpha) * filteredDiff;

// Reset filteredDiff if invalid (NaN or Inf)
if (isnan(filteredDiff) || isinf(filteredDiff)) {
filteredDiff = 0;
}

Serial.print("Filtered magnitude difference: ");
Serial.println(filteredDiff);

// Detect jump if threshold exceeded and cooldown elapsed
if (!isEffectRunning && filteredDiff > ACCEL_THRESHOLD && (now - lastTriggerTime > COOLDOWN_MS)) {
Serial.print("Jump detected! Starting effect: ");
Serial.println(EFFECTS[currentEffectIndex].name);
lastTriggerTime = now;
isEffectRunning = true;
EFFECTS[currentEffectIndex].startFunc();
}

// Run animation effect if active
if (isEffectRunning) {
bool done = EFFECTS[currentEffectIndex].stepFunc(now);
if (done) {
isEffectRunning = false;
strip.clear();
strip.show();
}
}

delay(20); // Loop delay for timing
}

// ------------------------
// Colorful Blink Effect
// ------------------------

uint32_t blinkColors[] = {
strip.Color(255, 0, 0), // Red
strip.Color(0, 255, 0), // Green
strip.Color(0, 0, 255) // Blue
};

int blinkStep = 0;

void startColorfulBlink() {
blinkStep = 0;
effectTimer = millis();
}

bool stepColorfulBlink(unsigned long now) {
int totalSteps = BLINK_TIMES * 6; // 3 colors * ON/OFF * times

if (blinkStep >= totalSteps) return true; // Animation finished

int stepIndex = blinkStep / 2; // Each color has ON and OFF step
bool onStep = (blinkStep % 2) == 0;

unsigned long duration = onStep ? BLINK_ON_DURATION_MS : BLINK_OFF_DURATION_MS;

if (now - effectTimer >= duration) {
effectTimer = now;

if (onStep) {
int colorIndex = stepIndex % 3;
strip.fill(blinkColors[colorIndex]);
strip.show();
} else {
strip.clear();
strip.show();
}

blinkStep++;
}

return false; // Animation ongoing
}

// ------------------------
// Rainbow Wave Effect
// ------------------------

int rainbowCycle = 0;
int rainbowHue = 0;

void startRainbowWave() {
rainbowCycle = 0;
rainbowHue = 0;
effectTimer = millis();
}

bool stepRainbowWave(unsigned long now) {
if (rainbowCycle >= RAINBOW_CYCLES) return true; // Animation done

if (now - effectTimer >= RAINBOW_STEP_DELAY_MS) {
effectTimer = now;

for (int i = 0; i < strip.numPixels(); i++) {
int pixelHue = rainbowHue + (i * 65536L / strip.numPixels());
uint32_t color = strip.gamma32(strip.ColorHSV(pixelHue));
strip.setPixelColor(i, color);
}
strip.show();

rainbowHue += 256;
if (rainbowHue >= 65536) {
rainbowHue = 0;
rainbowCycle++;
}
}

return false; // Animation ongoing
}

Downloads

Uploading to Arduino

Skjermbilde 2025-07-20 162739.png
Skjermbilde 2025-07-20 155722.png

Connect the Arduino to your computer using the USB-A to USB-B cable, then press upload. For more information about this, see here. Some small yellow LEDs should start flashing on the Arduino, indicating that it is receiving data from your computer. Then, when they stop, you should see a "Uploading complete" message on your computer like in the image above.

Testing and Troubleshooting

20250720_155536(1).gif
Skjermbilde 2025-07-20 160543.png

Congrats again! You should now be done with the electronics! To test, try moving the GY-521, and the LEDs should start blinking. If they do: Amazing!, If not, then:

  1. Check that all wiring is correct
  2. Check that no cables are broken, try and replace a few, plug out and in.
  3. Check that serial output looks correct (see image for how to open)
  4. Comment here and I¨ll try to help

How It Works

Before we continue, let’s take a moment to understand how the system works.

This project uses two main components:

  1. A GY-521 motion sensor (IMU) that detects when you move or jump.
  2. A WS2812B LED strip that lights up in different effects based on that motion.

The GY-521 measures acceleration in three dimensions using an MPU6050 chip. When you make sudden moves, the sensor detects a spike in acceleration. This data is sent to the Arduino UNO, which checks if the accelration is strong enough to trigger the LED effects.

If it is, the Arduino sends signals to the LED strip using a special timing protocol through digital pin 6. This creates a flashing effect or a rainbow animation, depending on your settings.

In short:

Jump detected ➜ Arduino calculates ➜ LEDs flash or animate


Customizing the Code (Optional)

At the top of the code, you’ll find easy-to-change settings like which effect runs when you jump, how sensitive the jump detection is, and how many LEDs your strip has. Feel free to tweak these to fit your project.

If you want to make your hat truly unique, you can customise the code even further — but fair warning, this is for the more adventurous tinkerers out there!

To add your own lighting effects:

  1. Write two new functions — one to start your effect and another to update it frame-by-frame until it’s done.
  2. Add your effect to the list of available effects in the code.
  3. Change the selected effect number to point to your new effect.

Going Off Grid

20250720_162202.jpg
20250720_162211.jpg
20250720_162206.jpg
20250720_161542.jpg

Right now, the Arduino is powered from our computer, but it would be annoying to have to carry that everywhere. Therefore, we will use a 9V battery and a connector. First, unplug the Arduino from your computer! This is very important because we can damage the Arduino if we power it from two places simultaneously.


If you have a 9V battery connector with a barrel jack, then just connect that to the barrel jack port on the Arduino and you are done!


If not, connect the red cable from the 9V battery to the Arduino port marked VIN. Then connect the black wire from the 9V battery to any of the Arduino's GND ports. Now the Arduino should work just like when it was connected to the computer, but without that long wire!

If not, try switching to another full 9V battery or ask for help in the comments below.

Printing the Hat

20250721_224601.jpg
20250721_224605.jpg

Import all files below into your slicer of choice and print them on your 3D printer. You will need a printer with a build plate of 300x300mm. The pieces marked: Support Required, need support. Just automatic tree-supports should work just fine. You do not need to tweak any other settings.

Removing Supports

20250721_225357.jpg
20250721_224632.jpg
20250721_225426.jpg

When the parts are done printing, remove all supports using pliers. Remember to also clear debris that could be in the screw holes. Now we are almost done!

Assembling the Hat

flosshat-v9-ezgif.com-video-to-gif-converter.gif
20250721_230906.jpg
20250721_231506.jpg
20250721_232208.jpg

It is time to assemble the hat! ⚡Push all the pieces together like shown in the videos above. You can use glue to secure the pieces in place, but the friction fit should work just fine. You probably won't need to secure the electronics bay with all nine screws, but use at least four.


Gif tutorials that didn't fit here because of file size can be found here.


Unplug the 9V battery before inserting the electronics. All electronics fit friction-fitted into the bay, except for the Arduino, which is secured with its screw holes. Remember not to overtighten, as that can damage the Arduino. Before closing it up, plug in the 9V battery and check that the circuit is still working. If not, don't panic! Just revert to step 13. You made it work once; you can do it again! 🥇


Depending on your printer tolerances, you may need to use some tape to secure the top. Just make sure you can open it up again! You will have to unplug the 9V battery when not in use.

Attaching the Led Lights

20250721_233247(1).gif
20250721_232809.jpg
a914aea4-55b9-4ae0-accc-9fd4be705a39.jpg

Now we attach the star of the show, the lights! Here, you can either use double-sided tape or just hot glue. We used hot glue.


You can make your own pattern down the hat or just use ours. Remember to not have the Arduino powered while securing the strip, to prevent it from getting damaged.

Enjoy It!

20250721_235049(1).gif
20250721_235355(1).jpg
20250721_235346.jpg
20250721_235355(2).jpg
20250721_235355(3).jpg

Jump around, sync it to your moves or jump into the code to make the hat truly yours! 🥇✨🛠️


I hope you have enjoyed this instructable ✨, and if you have any questions, leave them in the comments!

Optional Learn the Process

For those interested, here is how the hat was made using Autodesk Fusion! 🛠️

Planning

20250719_180019.jpg
20250722_014229.jpg

The first thing we did when making the hat was draw a reference. This makes it much easier to model in Fusion because you essentially split the designing and modelling into two separate parts.


The next step was to figure out the head form. We did this by taking a steel wire and wrapping it around our heads. This also helped us visualise the design.

Modeling in Autodesk Fusion

image.png

Now that we had both the head dimensions and a design plan, it was time to start modelling. We started by sketching the base using the dimensions we had obtained.


Then we extruded it 2 mm. The idea behind this was based on a 3D printing levelling test where you make a square 2 mm thick. When it's printed, it becomes the perfect form for a hat. We then extruded the middle up 10 cm to give it that fancy look.

Modeling in Autodesk Fusion

hat2.png

After we had created the basic outline of the hat, we split it into two parts using the split body tool. We split the top to make the printing of the top part flush with the printing surface, giving it a better texture. After that, we added mounting parts on the bottom.

Modeling in Autodesk Fusion

hat4.png

Now we had a problem on our hands. The bottom was too big to print in one part, so we had to split it using the same tool from earlier and create similar mounting studs.

Modeling in Autodesk Fusion

hat3.png


To make the parts fit into each other, we used the merge tool with keep tools and cut operation. This ensured that even when we changed part of the design, we could be sure it would fit. We also used offset face to account for small inaccuracies with 3D printers.

Modeling in Autodesk Fusion

hat.png


What we mostly did to leverage each other's skills was have one person design the hat and one work on the light part. The smart thing here was that by using Fusion, when one was done designing, using the timeline and workspace together made it easy to add the mounts to fit the electronics afterwards.


If you would like to inspect the file yourself, you can download it below.