ESE 5190 · Team 09 · F25 · UPenn

dAnciNG

MACHINE NOT LEARNING

9 Dance Zones
4 LED Strips
173 MIDI Notes
ATmega 328PB × 2
Watch Demo Explore Hardware

What Is dAnciNG Machine?

The Concept

A portable music game mat powered by ATmega328PB. Players step in rhythm with preloaded songs across a 3×3 grid. Each correct step triggers full LED illumination and synchronized sound feedback via PWM buzzer, while a TFT LCD tracks real-time scores.

The Motivation

Commercial arcade dance machines are expensive, bulky, and inaccessible for home use. We built a portable, affordable alternative that delivers the same rhythm-game experience — integrating hardware design, sensor systems, and human–computer interaction into a compact mat.

Key Innovation
  • Dual ATmega328PB architecture for timing isolation
  • MIDI-extracted melody synchronized with PC BGM
  • SPI-driven SK6812 RGBW LED strips for visual cues
  • MUX-based 9-channel pressure sensing via ADC
  • Python-automated pitch extraction via librosa pYIN
Gameplay Loop
  • PC streams background music (BGM)
  • Firmware predicts next note → LED hint (white, half-bright)
  • Player steps on correct block → LED goes full red
  • Buzzer plays matching pitch in sync with music
  • Score increments; LCD updates in real-time

How It Works

🎵
PC plays BGM
Song streams from computer in real time
▶▶
💡
LED Hint
Block lights white 0.5s before expected beat
▶▶
👟
Foot Step
FSR ADC detects pressure on correct zone
🔴
LED Fires Red
Full block illuminates on correct hit
▶▶
🔊
Buzzer Tone
PWM outputs matching MIDI pitch
▶▶
🏆
Score +1
LCD refreshes score immediately

See It In Action

The 9-Zone Dance Mat

1
2
3
4
5
6
7
8
9

HINT   HIT   IDLE

Hardware Specs

MCU ATmega328PB × 2
Pressure Sensor FSR × 9 via 74HC4051 MUX
ADC Channel ADC7 — 10-bit resolution
LED Strips SK6812 RGBW × 4 (60 LED each)
LED Protocol 800 kHz bit-bang GPIO
Display 1.8" ST7735 TFT via SPI
Audio PWM buzzer via Timer0 OC0B
Power 5V USB + 3.3V LDO regulated
Inter-MCU Comm UART1 command frames

Dual-MCU Architecture

BOARD_A // MAIN_CONTROLLER
Application State Machine
Runs the core game loop: MIDI playback via Timer1 CTC (1ms ISR), ADC-gated pressure sensing, score tracking, LCD updates via SPI, and UART1 command dispatch to Board B.
Timer1 CTC ADC7 SPI/LCD UART1
BOARD_B // LED_CONTROLLER
LED Strip Driver
Dedicated receiver loop that decodes single-byte command frames from Board A and drives four SK6812 strips independently via GPIO (PC0–PC3) using cycle-accurate AVR bit-timing.
GPIO PC0-PC3 SK6812 UART RX
TIMING_ENGINE // REAL_TIME
Timer1 — Note Scheduler
CTC mode generates 1ms interrupt. ISR decrements remaining_ms for note duration tracking and asserts sample_flag for periodic ADC polling without busy-waiting.
CTC Mode 1ms ISR sample_flag
AUDIO_ENGINE // PWM
Timer0 — MIDI Playback
Fast PWM on OC0B. MIDI notes converted to frequencies via exponential formula, then to Timer0 TOP values. 173-note Mario melody stored as PROGMEM arrays to conserve SRAM.
Fast PWM OC0B / PD5 PROGMEM

MIDI → Frequency → PWM Conversion

// MIDI note to frequency (exponential mapping)
f = 440 × 2^((midi - 69) / 12)

// Frequency to Timer0 TOP value (Fast PWM, prescaler=8)
TOP = F_CPU / (2 × prescaler × freq) - 1

// ADC-based deadlock fix: use interrupt-driven sampling
// instead of blocking ADC reads to prevent Timer1 race condition
ISR(ADC_vect) {
  adc_result = ADC;
  adc_ready = true;
}

SRS / HRS Results

Software Requirements

IDRequirementStatusOutcome
SRS-01Pressure sensors sampled every 50 ms ±10 msConfirmedISR at 5ms implemented for stable sampling
SRS-02LED hint ~0.5s before step; full light on correct hitConfirmedBlock lighting synced with UI and BGM
SRS-03LCD score refresh within 1s of stepConfirmedImmediate feedback imperceptible to human eye
SRS-04Final score displayed within 3s of song endConfirmedImmediate display at song completion
SRS-05PWM buzzer plays correct pitch only on valid stepConfirmedCorrect step → tone; incorrect step → silence
SRS-06Button selects difficulty / switches songsModifiedRepurposed as start/reset button for sync reliability

Hardware Requirements

IDRequirementStatusOutcome
HRS-01ATmega328PB accurate timing for LED/sound syncConfirmedDemo shows full lockstep between LED, speaker, LCD, and sensors
HRS-02FSR detects 0–686N, sensitivity ≥10 mV/NConfirmedADC threshold 850/1024 reliably distinguishes press/no-press
HRS-03LED latency <50ms, 9 distinct colorsConfirmedWhite hint → red hit transition verified in demo
HRS-04Speaker 200Hz–5kHz, ≥90dB at 0.5mConfirmedCorrect pitch at high volume synchronized with BGM
HRS-05LCD score refresh ≤200msConfirmedScore updates immediately on each scoring event
HRS-06Button for song/difficulty switchingConfirmedButton resets score and restarts game loop

Sprint Timeline

SPRINT_01
Core Modules
LCD, LED, and audio processing completed. Python pitch extraction via librosa pYIN. PWM timer validated with oscilloscope.
SPRINT_02
Melody + Pressure Integration
Melody engine, buzzer PWM, and MUX-based ADC pressure sensing integrated. Full melody playback with ADC-gated triggering demonstrated end-to-end.
SPRINT_03
SK6812 LED Driver + LCD
SK6812 driver at 2.4 MHz with 3-SPI-bit encoding. ST7735 LCD integrated with custom graphics library for score display.
MVP_DEMO
Full System Demo
Dual-board UART architecture deployed. PC + MCU in full audio lockstep. Hint → step → LED flash → buzzer → score loop confirmed stable.
BUG_FIX
ADC Deadlock Resolution
System froze due to race between Timer1 ISR and blocking ADC_read(). Fixed by switching to interrupt-driven ADC sampling.
FINAL_DEMO
Full Game Complete
Complete Mario melody across 9 zones, four LED strips, real-time scoring, start/reset button. Stable across full song duration.

What We Learned

Timing is Everything
60% of bugs came from oversight; 40% from unknown hardware constraints. Blocking ADC reads caused a deadlock with Timer1 ISR. Never mix blocking calls with ISR-shared state — use interrupt-driven I/O.
🧱
Architecture First
Initial single-board design had unresolvable timing conflicts. Refactoring to a two-board UART architecture mid-project was painful. Design system boundaries before writing firmware.
🔬
Measure, Don't Guess
Oscilloscope and logic analyzer revealed LED timing jitter and SPI delays affecting note durations. printf debugging over UART1 proved essential for real-time state inspection without halting execution.
💾
RAM is Precious
ATmega328PB SRAM is limited. Moving large note/duration arrays to PROGMEM with pgm_read accessors was mandatory. All floating-point was replaced with fixed-point integer math.
🎮
What Went Well
MUX + SPI subsystems worked smoothly. PC–MCU audio lockstep — when stepping on a correct block, the buzzer pitch aligns exactly with the BGM note. Most satisfying engineering achievement of the project.
🚀
Next Steps
Replace direct PWM square-wave buzzer with DAC + amplifier for richer audio. Improve FSR wiring stability. Expand music library beyond Mario. Add difficulty levels with multiple preloaded songs.

The Crew

🎮
Xiang Ding
Yang Xing
🎵
Shang Wang