Imagine being the life of the party on New Year's Eve, with a hat that's as dynamic and energetic as the celebration itself. Introducing Hat Beat, a wearable innovation that fuses music, lights, and fireworks animation at midnight. This cutting-edge party hat features a built-in microphone that captures the music, triggering a captivating LED light showbase that pulses and flashes in perfect harmony with the beat. As the clock strikes midnight, Hat Beat senses the final moments of the old year and bursts into a stunning display of LED fireworks, guaranteed to get everyone moving and grooving.
Get ready to elevate your New Year's Eve celebration and make a bold statement with Hat Beat, the ultimate party accessory designed to light up the night!
HardwareFirst, we’ve cut our Infineon S2GO MEMSMIC IM69D13 microphone to its dual outputs. One side handles I2S audio, perfect for high-quality sound, while the other side manages PDM output, ideal for simpler processing in embedded systems - just what we need for this project!
We also use an Infineon CY8CPROTO-062-4343W development board with built-in Wi-Fi, which serves as the microcontroller for the project and ensures a fast, stable connection. The WS2812B RGB LEDs on the flexible matrix are used to display music visualizations and effects.
Next, we 3D printed our custom hat and glued the 32x8 flexible LED matrix. To give it a sleeker appearance, we wrapped everything in black fabric for a more stylish finish. [slide pictures]
To bring our project to life, we need to properly connect the controller, microphone, and LED lights. Both the microphone and LED lights require a power supply and ground connection, while the microphone also needs to be connected to the controller using PDM CLK and DATA pins to transmit audio signals. In contrast, the LED lights only require a single DI Data Input pin connection to receive data from the controller. By following these wiring guidelines, also shown in the picture below, we can ensure a seamless and efficient setup, paving the way for a successful project.
The project runs on MicroPython, which simplifies hardware control and accelerates development. For setting this up just follow the link for the getting started instructions.
Two main functions are implemented in the software: the VU-Animation and the Firework Animation.
Real-life VU Animation:For the VU animation, we use a standard LED display set to 32 LEDs on the X-axis and 8 LEDs on the Y-axis. Our microphone samples the audio and defines its intensity by illuminating a corresponding number of LEDs, creating a dynamic VU meter animation that loops along the X-axis. The LEDs light up in response to the audio signal, providing a visually engaging experience that synchronizes with the music.
For the fireworks animation, we arrange the LEDs differently, using 8 LEDs on the X-axis and 32 LEDs on the Y-axis. The display will look something like this:
We draw a firework explosion and fix its initial coordinates like here:
To create the animation, we increment the Y-axis coordinates in a loop, simulating the movement of fireworks across the display.
To ensure the fireworks go off exactly at midnight, we track the time using network connectivity and extract the current time from the RTC (Real-Time Clock) library. This precise timing ensures that the fireworks display aligns perfectly with the New Year's countdown.
A detailed, step-by-step explanation of the code is provided here:
Code Walkthroughfrom machine import Pin, PDM_PCM, freq, bitstream, AUDIO_PDM_24_576_000_HZ, RTC
import time
import array
import math
import random
import network
import ntptime
import utime
This code imports various modules required for hardware control, timing, and networking. This includes modules for handling pins, PDM/PCM audio, frequency settings, bitstream operations, real-time clock (RTC), and libraries for mathematical operations, random number generation, networking, and time synchronization.
Constants Definition
# Constants
FRAME_SIZE = 1024
SAMPLE_RATE_HZ = 8000
DECIMATION_RATE = 64
AUDIO_SYS_CLOCK_HZ = 24576000
MICROPHONE_GAIN = 6 # Adjusted for lower sensitivity
VOLUME_RATIO = 8 * FRAME_SIZE # Increased to reduce sensitivity
Here, several constants are defined to configure the audio processing. FRAME_SIZE
refers to the size of audio frames, SAMPLE_RATE_HZ
is the audio sample rate, and DECIMATION_RATE
is used for down-sampling the audio signal. AUDIO_SYS_CLOCK_HZ
sets the audio system clock frequency, MICROPHONE_GAIN
adjusts the microphone sensitivity, and VOLUME_RATIO
helps in normalizing the volume.
LED Matrix Setup
# LED matrix setup
DIN1 = Pin('P9_1', Pin.OUT, value=0)
led_data = bytearray([0] * (num_leds * 3))
TIMING = [400, 850, 800, 450]
num_leds = 32 * 8
This snippet initializes the LED matrix. DIN1
is the data input pin for the LEDs, and led_data
is a bytearray to hold the color data for each LED. TIMING
defines the signal timing for controlling the LEDs. num_leds
specifies the total number of LEDs in the matrix.
Initialize Audio System Clock and PDM/PCM
# Initialize audio system clock
freq(AUDIO_SYS_CLOCK_HZ)
# Initialize PDM/PCM
print("PDM_PCM Initialization")
pdm_pcm = PDM_PCM(
0,
sck="P10_4",
data="P10_5",
sample_rate=SAMPLE_RATE_HZ,
decimation_rate=DECIMATION_RATE,
bits=PDM_PCM.BITS_16,
format=PDM_PCM.MONO_LEFT,
left_gain=MICROPHONE_GAIN,
right_gain=MICROPHONE_GAIN,
)
pdm_pcm.init()
print("PDM initialized successfully")
The audio system clock is set to AUDIO_SYS_CLOCK_HZ
. Then, the PDM/PCM (Pulse Density Modulation/Pulse Code Modulation) interface is initialized with specific parameters, such as the clock pin (sck
), data pin (data
), sample rate, decimation rate, bit depth, and microphone gain. The pdm_pcm
object is then initialized.
Functions for LED Control
# Function to manually light all LEDs
def manual_light_all(buf):
bitstream(DIN1, 0, TIMING, buf)
# Function to create color with adjustable brightness
def create_color(red, green, blue, brightness=1.5):
red = int(red * brightness)
green = int(green * brightness)
blue = int(blue * brightness)
return bytearray([green, red, blue])
# Function to calculate color based on volume
def get_color_by_position(x, y, max_y):
hue = (x + y) / max_y
r = int((math.sin(2 * math.pi * hue) + 1) * 127.5)
g = int((math.sin(2 * math.pi * (hue + 1 / 3)) + 1) * 127.5)
b = int((math.sin(2 * math.pi * (hue + 2 / 3)) + 1) * 127.5)
brightness = 0.1 + 0.9 * (y / max_y)
return create_color(r, g, b, brightness)
# Function to set the color of a specific LED
def set_led_vu(x, y, color):
if 0 <= x < 32 and 0 <= y < 8:
if x % 2 == 0:
index = (x * 8) + y # Normal order for even columns
else:
index = (x * 8) + (7 - y) # Reverse order for odd columns
led_data[index * 3: (index + 1) * 3] = color
def set_led_fireworks(x, y, color):
if 0 <= x < 32 and 0 <= y < 8:
index = (y * 32) + x
led_data[index * 3: (index + 1) * 3] = color
Several functions are defined to control the LEDs:
manual_light_all(buf)
: Sends the bitstream to light up all LEDs based onbuf
.create_color(red, green, blue, brightness=1.5)
: Creates a color with adjustable brightness.get_color_by_position(x, y, max_y)
: Calculates color based on the position and maximum Y value.set_led_vu(x, y, color)
: Sets the color of a specific LED based on its position.set_led_fireworks(x, y, color)
: Sets the color of an LED for fireworks, considering a different indexing method.
Updating LED Matrix Based on Volume
# Function to update LED matrix based on volume
def update_led_matrix(volume, current_x):
num_leds_to_light = min(volume // VOLUME_RATIO, 8)
for y in range(8):
if y < num_leds_to_light:
color = get_color_by_position(current_x, y, 8)
set_led_vu(current_x, y, color) # Color based on volume level
else:
set_led_vu(current_x, y, create_color(0, 0, 0)) # Turn off the LED
manual_light_all(led_data)
current_x = (current_x + 1) % 32 # Move to the next column
return current_x
This function updates the LED matrix based on the audio volume. It calculates how many LEDs to light by dividing the volume by VOLUME_RATIO
and ensures it doesn't exceed 8. Then, it sets the color of the LEDs in the current column based on the volume and moves to the next column for the next update.
Fireworks Animation Functions
def trail_effect(x, y, color, delay=0.05):
for factor in [0.8, 0.6, 0.4, 0.2]:
set_led_fireworks(x, y, dim_color(color, factor))
manual_light_all(led_data)
time.sleep(delay)
def launch_firework(start_y, color, delay=0.05):
for x in range(16, 21): # Longer launch sequence
clear_leds(num_leds)
set_led_fireworks(x, start_y, color)
trail_effect(x - 1, start_y, dim_color(color, 0.5), delay)
manual_light_all(led_data)
time.sleep(delay)
def star_explosion(y, colors, delay=0.05):
first_group_coords = [
(11, y-1), (27, y-1), (26, y-1), (28, y-1),
(22, y-1), (10, y-1), (12, y-1), (18, y-1),(21,y-1),(19,y-1),(20, y-1)
]
remaining_coords = [
# Third
(4, y-1),
# Eighth
(4, y),
# Fourth
(2, y-1),
# Second
(6, y-1),
# Fifth
(17, y-1),
# First
(23, y-1),
# Sixth
(2, y),
# Seventh
(6, y)
]
yellow_color = create_color(255, 255, 0)
always_yellow_coords = [(22, y-1), (4, y), (4, y-1), (18, y-1),(26, y-1),(10, y-1),(28, y-1),(12, y-1)]
clear_leds(num_leds)
manual_light_all(led_data)
time.sleep(delay)
for color in colors:
for (sx, sy) in first_group_coords:
if (sx, sy) in always_yellow_coords:
set_led_fireworks(sx, sy, yellow_color)
else:
if 0 <= sx < 32 and 0 <= sy < 8:
set_led_fireworks(sx, sy, color)
manual_light_all(led_data)
time.sleep(delay)
for color in colors:
for (sx, sy) in remaining_coords:
if (sx, sy) not in first_group_coords:
if (sx, sy) in always_yellow_coords:
set_led_fireworks(sx, sy, yellow_color)
else:
if 0 <= sx < 32 and 0 <= sy < 8:
set_led_fireworks(sx, sy, color)
manual_light_all(led_data)
time.sleep(delay)
clear_leds(num_leds)
These functions create a fireworks effect on the LED matrix:
trail_effect()
: Creates a trailing effect for a firework.launch_firework()
: Launches a firework from a given starting position.star_explosion()
: Creates a star explosion effect with specified colors.
Additional Utility Functions
def shuffle_list(lst):
"""Shuffle the list in place."""
for i in range(len(lst) - 1, 0, -1):
j = random.randint(0, i)
lst[i], lst[j] = lst[j], lst[i]
def explode_firework(colors, delay=0.05):
positions = list(range(3, 7)) # Positions 4, 5, 6, and 7
shuffle_list(positions)
for x in positions:
launch_firework(x - 1, create_color(255, 255, 255), delay)
star_explosion(x, colors, delay)
clear_leds(num_leds)
def fireworks_animation(cycles=10, delay=0.05):
colors = [
create_color(255, 0, 0), create_color(0, 255, 0), create_color(0, 0, 255),
create_color(255, 255, 0), create_color(0, 255, 255), create_color(255, 0, 255),
create_color(255, 165, 0), create_color(255, 20, 147)
]
for _ in range(cycles):
firework_count = random.randint(2, 5)
for _ in range(firework_count):
explode_firework(colors, delay)
time.sleep(random.uniform(0.2, 1.2))
These utility functions support the fireworks effects:
shuffle_list()
: Shuffles a list of positions.explode_firework()
: Creates a firework explosion at random positions.fireworks_animation()
: Runs the fireworks animation for a specified number of cycles.
Networking Functions
# Function to connect to Wi-Fi
def network_connect():
wlan = network.WLAN(network.STA_IF)
if wlan.isconnected():
print('[Network] already connected')
return True
# Enable and connect to Wi-Fi
wlan.active(True)
wlan.connect('XXXX', '123456789') # Replace with your SSID and Password
# Wait for the connection to establish
utime.sleep(5)
for i in range(0, 100):
if not wlan.isconnected() and wlan.status() >= 0:
print("[Network] Waiting to connect..")
utime.sleep(2)
# Check connection
if not wlan.isconnected():
print("[Network] Connection failed!")
return False
else:
print("Connected to Wi-Fi. Network config:", wlan.ifconfig())
return True
# Function to synchronize time with NTP server and adjust for the local timezone
def sync_time(timezone_offset=0):
try:
print('Synchronizing time with NTP server...')
ntptime.host = 'pool.ntp.org' # You can use another NTP server if preferred
ntptime.settime()
print('Time synchronized.')
# Adjust time for timezone
rtc = RTC()
current_time = list(rtc.datetime())
current_time[4] = (current_time[4] + timezone_offset) % 24
rtc.datetime(tuple(current_time))
print('Time adjusted for timezone.')
except Exception as e:
print('Failed to synchronize time:', e)
# Function to get the current time
def get_current_time():
rtc = RTC()
current_time = rtc.datetime()
formatted_time = '{:02}:{:02}'.format(current_time[4], current_time[5])
formatted_date = '{:04}-{:02}-{:02}'.format(current_time[0], current_time[1], current_time[2])
return formatted_date, formatted_time
These functions handle network connectivity and time synchronization that we have seen in one of our previous projects:
network_connect()
: Connects to a Wi-Fi network using the specified SSID and password.sync_time()
: Synchronizes the device time with an NTP server and adjusts for the local timezone.get_current_time()
: Retrieves the current date and time from the RTC.
Main Loop
# Main loop
def main():
current_x = 0
while True:
# Read audio samples into buffer
rx_buf = array.array('h', [0] * FRAME_SIZE)
num = pdm_pcm.readinto(rx_buf)
if num is None or num == 0:
print("PDM read failed or no data")
continue
volume = 0
audio_count = num // 2 # Each sample is 2 bytes
# Calculate the volume by summing the absolute values of all audio samples
volume = sum(abs(rx_buf[i]) for i in range(audio_count))
# Update LED matrix based on the calculated volume
current_x = update_led_matrix(volume, current_x)
if network_connect():
# Set the timezone offset (e.g., +1 for UTC+1)
timezone_offset = 1
sync_time(timezone_offset)
while True:
date, time = get_current_time()
# print("Current date:", date, "Current time:", time) #to check
# Check if it is time for the fireworks display
if time == '00:00': # Adjust the time as needed
fireworks_animation(cycles=7, delay=0.003)
utime.sleep(1)
else:
current_x = update_led_matrix(volume, current_x)
if __name__ == '__main__':
main()
The main()
function is the core loop of the program. It continuously reads audio data, calculates the volume, and updates the LED matrix based on the volume. If a network connection is established, it synchronizes the time and checks periodically if it is time to run the fireworks animation. The fireworks animation is triggered at a specific time of the day.
As we wrap up our journey with Hat Beat, we hope you're as excited about this project as we are. Hat Beat is more than just a hat; it's a fusion of lights, music, and fun that promises to make any celebration unforgettable. Whether you're ringing in the New Year or enjoying a party, Hat Beat is sure to make you stand out and keep everyone dancing.
Happy New Year and Best wishes!
Comments