05 - Lab Digital Music DJ Controller

# Lab: **Digital Music DJ Controller - Real-Time Music Control via Serial Commands** ## **Objective** Create an interactive music player system using the TK37 passive buzzer with real-time serial control. Learn to implement user interface design, state management, tempo control, and interactive musical features through serial commands. ## **Required Components** ![](https://cdn.shopify.com/s/files/1/0331/9994/7908/files/Pasted_image_20250723155115.png?v=1753858090) 1. **Lonely Binary UNO R3** - Main Arduino board 2. **TinkerBlock UNO R3 Shield** - Expansion shield that plugs onto the UNO R3 3. **TinkerBlock TK37** - Passive Buzzer Module ## **Theory** ### **Interactive Music Systems** - **Real-time Control**: Immediate response to user commands - **State Management**: Track current system status (playing, paused, stopped) - **Command Processing**: Parse and execute user input - **Feedback Systems**: Provide user with system status information ### **Serial Communication** - **Command Interface**: Text-based control system - **Input Parsing**: Convert text commands to musical actions - **Status Reporting**: Display current system state - **Error Handling**: Manage invalid or unexpected input ### **Musical Control Concepts** - **Tempo Control**: Adjustable speed (BPM - Beats Per Minute) - **Note Selection**: Individual note playback - **Melody Control**: Complete song playback - **Volume Simulation**: Control through frequency and duration ## **Wiring Instructions** ### **TK37 Passive Buzzer Pinout** - **GND** → Arduino GND - **VCC** → Arduino 5V - **NC** → No Connection - **Signal** → Arduino Digital Pin D7 ### **Connection Diagram** ![](https://cdn.shopify.com/s/files/1/0331/9994/7908/files/Pasted_image_20250723155157.png?v=1753858115) ``` UNO R3 + Shield └── Digital Pin D7 ──→ TK37 Signal ``` ## **Interactive Music Player with Serial Control** ```cpp // Interactive Music Player with TK37 Passive Buzzer // Pin definitions #define BUZZER_PIN 7 // Musical note frequencies (simplified set) #define NOTE_C4 262 #define NOTE_D4 294 #define NOTE_E4 330 #define NOTE_F4 349 #define NOTE_G4 392 #define NOTE_A4 440 #define NOTE_B4 494 #define NOTE_C5 523 #define NOTE_D5 587 #define NOTE_E5 659 #define NOTE_F5 698 #define NOTE_G5 784 #define NOTE_A5 880 #define NOTE_B5 988 // Note duration constants #define WHOLE_NOTE 1600 #define HALF_NOTE 800 #define QUARTER_NOTE 400 #define EIGHTH_NOTE 200 #define SIXTEENTH_NOTE 100 // Music player states #define STATE_IDLE 0 #define STATE_PLAYING 1 #define STATE_PAUSED 2 // Variables int currentState = STATE_IDLE; int currentTempo = 120; // BPM int currentVolume = 50; // 0-100 bool loopMode = false; void setup() { Serial.begin(9600); pinMode(BUZZER_PIN, OUTPUT); delay(1000); Serial.println("TK37 Interactive Music Player"); Serial.println("============================="); Serial.println("Commands:"); Serial.println("1-8: Play notes C4-B4"); Serial.println("q-w: Play notes C5-B5"); Serial.println("a: Play ascending scale"); Serial.println("d: Play descending scale"); Serial.println("t: Play Twinkle Twinkle"); Serial.println("h: Play Happy Birthday"); Serial.println("j: Play Jingle Bells"); Serial.println("s: Stop all sounds"); Serial.println("p: Pause/Resume"); Serial.println("l: Toggle loop mode"); Serial.println("+: Increase tempo"); Serial.println("-: Decrease tempo"); Serial.println("v: Adjust volume"); Serial.println("r: Random melody"); Serial.println("c: Custom frequency"); Serial.println("?: Show help"); Serial.println(); displayStatus(); } void loop() { if (Serial.available()) { char command = Serial.read(); handleCommand(command); } // Small delay for stability delay(10); } void handleCommand(char command) { switch (command) { // Play individual notes (C4-B4) case '1': playNote(NOTE_C4, QUARTER_NOTE); break; case '2': playNote(NOTE_D4, QUARTER_NOTE); break; case '3': playNote(NOTE_E4, QUARTER_NOTE); break; case '4': playNote(NOTE_F4, QUARTER_NOTE); break; case '5': playNote(NOTE_G4, QUARTER_NOTE); break; case '6': playNote(NOTE_A4, QUARTER_NOTE); break; case '7': playNote(NOTE_B4, QUARTER_NOTE); break; case '8': playNote(NOTE_C5, QUARTER_NOTE); break; // Play individual notes (C5-B5) case 'q': playNote(NOTE_C5, QUARTER_NOTE); break; case 'w': playNote(NOTE_D5, QUARTER_NOTE); break; case 'e': playNote(NOTE_E5, QUARTER_NOTE); break; case 'r': playNote(NOTE_F5, QUARTER_NOTE); break; case 't': playNote(NOTE_G5, QUARTER_NOTE); break; case 'y': playNote(NOTE_A5, QUARTER_NOTE); break; case 'u': playNote(NOTE_B5, QUARTER_NOTE); break; // Play scales case 'a': playAscendingScale(); break; case 'd': playDescendingScale(); break; // Play melodies case 't': playTwinkleTwinkle(); break; case 'h': playHappyBirthday(); break; case 'j': playJingleBells(); break; // Control commands case 's': stopSound(); break; case 'p': togglePause(); break; case 'l': toggleLoopMode(); break; case '+': increaseTempo(); break; case '-': decreaseTempo(); break; case 'v': adjustVolume(); break; case 'r': playRandomMelody(); break; case 'c': playCustomFrequency(); break; case '?': showHelp(); break; } } void playNote(int frequency, int duration) { if (currentState != STATE_PAUSED) { Serial.print("Playing note: "); Serial.print(frequency); Serial.println(" Hz"); tone(BUZZER_PIN, frequency, duration); delay(duration + 50); noTone(BUZZER_PIN); } } void playAscendingScale() { Serial.println("Playing ascending C major scale"); currentState = STATE_PLAYING; int notes[] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5}; int adjustedDuration = QUARTER_NOTE * 120 / currentTempo; for (int i = 0; i < 8; i++) { if (currentState == STATE_PAUSED) break; tone(BUZZER_PIN, notes[i], adjustedDuration); delay(adjustedDuration + 50); } noTone(BUZZER_PIN); currentState = STATE_IDLE; } void playDescendingScale() { Serial.println("Playing descending C major scale"); currentState = STATE_PLAYING; int notes[] = {NOTE_C5, NOTE_B4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_D4, NOTE_C4}; int adjustedDuration = QUARTER_NOTE * 120 / currentTempo; for (int i = 0; i < 8; i++) { if (currentState == STATE_PAUSED) break; tone(BUZZER_PIN, notes[i], adjustedDuration); delay(adjustedDuration + 50); } noTone(BUZZER_PIN); currentState = STATE_IDLE; } void playTwinkleTwinkle() { Serial.println("Playing: Twinkle Twinkle Little Star"); currentState = STATE_PLAYING; int melody[] = { NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4, NOTE_A4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4, NOTE_C4 }; int durations[] = { QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, HALF_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, HALF_NOTE }; playMelodyArray(melody, durations, 14); currentState = STATE_IDLE; } void playHappyBirthday() { Serial.println("Playing: Happy Birthday"); currentState = STATE_PLAYING; int melody[] = { NOTE_C4, NOTE_C4, NOTE_D4, NOTE_C4, NOTE_F4, NOTE_E4, NOTE_C4, NOTE_C4, NOTE_D4, NOTE_C4, NOTE_G4, NOTE_F4 }; int durations[] = { QUARTER_NOTE, EIGHTH_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, HALF_NOTE, QUARTER_NOTE, EIGHTH_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, HALF_NOTE }; playMelodyArray(melody, durations, 12); currentState = STATE_IDLE; } void playJingleBells() { Serial.println("Playing: Jingle Bells"); currentState = STATE_PLAYING; int melody[] = { NOTE_E4, NOTE_E4, NOTE_E4, REST, NOTE_E4, NOTE_E4, NOTE_E4, REST, NOTE_E4, NOTE_G4, NOTE_C4, NOTE_D4, NOTE_E4 }; int durations[] = { QUARTER_NOTE, QUARTER_NOTE, HALF_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, HALF_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, QUARTER_NOTE, HALF_NOTE }; playMelodyArray(melody, durations, 13); currentState = STATE_IDLE; } void playMelodyArray(int melody[], int durations[], int length) { for (int i = 0; i < length; i++) { if (currentState == STATE_PAUSED) break; int adjustedDuration = durations[i] * 120 / currentTempo; if (melody[i] == 0) { // REST delay(adjustedDuration); } else { tone(BUZZER_PIN, melody[i], adjustedDuration); delay(adjustedDuration + 50); } } noTone(BUZZER_PIN); } void stopSound() { noTone(BUZZER_PIN); currentState = STATE_IDLE; Serial.println("Sound stopped"); } void togglePause() { if (currentState == STATE_PLAYING) { currentState = STATE_PAUSED; noTone(BUZZER_PIN); Serial.println("Paused"); } else if (currentState == STATE_PAUSED) { currentState = STATE_PLAYING; Serial.println("Resumed"); } } void toggleLoopMode() { loopMode = !loopMode; Serial.print("Loop mode: "); Serial.println(loopMode ? "ON" : "OFF"); } void increaseTempo() { currentTempo += 10; if (currentTempo > 200) currentTempo = 200; Serial.print("Tempo: "); Serial.print(currentTempo); Serial.println(" BPM"); } void decreaseTempo() { currentTempo -= 10; if (currentTempo < 60) currentTempo = 60; Serial.print("Tempo: "); Serial.print(currentTempo); Serial.println(" BPM"); } void adjustVolume() { Serial.println("Volume adjustment not available on passive buzzer"); Serial.println("Use tone() frequency or duration to control volume"); } void playRandomMelody() { Serial.println("Playing random melody"); currentState = STATE_PLAYING; int notes[] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5}; int durations[] = {QUARTER_NOTE, EIGHTH_NOTE, HALF_NOTE}; for (int i = 0; i < 8; i++) { if (currentState == STATE_PAUSED) break; int randomNote = notes[random(8)]; int randomDuration = durations[random(3)]; int adjustedDuration = randomDuration * 120 / currentTempo; tone(BUZZER_PIN, randomNote, adjustedDuration); delay(adjustedDuration + 50); } noTone(BUZZER_PIN); currentState = STATE_IDLE; } void playCustomFrequency() { Serial.println("Enter frequency (20-20000 Hz):"); while (!Serial.available()) { delay(100); } int frequency = Serial.parseInt(); if (frequency >= 20 && frequency <= 20000) { Serial.print("Playing frequency: "); Serial.print(frequency); Serial.println(" Hz"); tone(BUZZER_PIN, frequency, 1000); delay(1050); noTone(BUZZER_PIN); } else { Serial.println("Invalid frequency. Range: 20-20000 Hz"); } } void showHelp() { Serial.println("TK37 Music Player Help:"); Serial.println("1-8: Play notes C4-B4"); Serial.println("q-w: Play notes C5-B5"); Serial.println("a: Ascending scale"); Serial.println("d: Descending scale"); Serial.println("t: Twinkle Twinkle"); Serial.println("h: Happy Birthday"); Serial.println("j: Jingle Bells"); Serial.println("s: Stop"); Serial.println("p: Pause/Resume"); Serial.println("l: Loop mode"); Serial.println("+/-: Tempo control"); Serial.println("r: Random melody"); Serial.println("c: Custom frequency"); Serial.println(); } void displayStatus() { Serial.println("Current Status:"); Serial.print("State: "); switch (currentState) { case STATE_IDLE: Serial.println("IDLE"); break; case STATE_PLAYING: Serial.println("PLAYING"); break; case STATE_PAUSED: Serial.println("PAUSED"); break; } Serial.print("Tempo: "); Serial.print(currentTempo); Serial.println(" BPM"); Serial.print("Loop: "); Serial.println(loopMode ? "ON" : "OFF"); Serial.println(); } ``` ## **Code Explanation** ### **State Management System** ```cpp #define STATE_IDLE 0 #define STATE_PLAYING 1 #define STATE_PAUSED 2 int currentState = STATE_IDLE; ``` - **State Tracking**: Monitors current system status - **IDLE State**: System ready for commands - **PLAYING State**: Currently playing music - **PAUSED State**: Music paused, can resume ### **Command Processing System** ```cpp void handleCommand(char command) { switch (command) { case '1': playNote(NOTE_C4, QUARTER_NOTE); break; case 'a': playAscendingScale(); break; case 's': stopSound(); break; // ... more commands } } ``` - **Character Input**: Single character commands for simplicity - **Switch Statement**: Efficient command routing - **Immediate Response**: Commands execute instantly - **Error Handling**: Invalid commands are ignored ### **Tempo Control System** ```cpp int currentTempo = 120; // BPM void increaseTempo() { currentTempo += 10; if (currentTempo > 200) currentTempo = 200; } ``` - **BPM Control**: Beats per minute for musical timing - **Range Limiting**: 60-200 BPM for musical practicality - **Dynamic Adjustment**: Real-time tempo changes - **Duration Calculation**: AdjustedDuration = baseDuration * 120 / currentTempo ### **Note Playing Functions** ```cpp void playNote(int frequency, int duration) { if (currentState != STATE_PAUSED) { Serial.print("Playing note: "); Serial.print(frequency); Serial.println(" Hz"); tone(BUZZER_PIN, frequency, duration); delay(duration + 50); noTone(BUZZER_PIN); } } ``` - **State Checking**: Only plays if not paused - **Feedback**: Shows frequency being played - **Timing Control**: Precise note duration - **Clean Stop**: noTone() ensures clean sound termination ### **Scale Playing Functions** ```cpp void playAscendingScale() { Serial.println("Playing ascending C major scale"); currentState = STATE_PLAYING; int notes[] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5}; int adjustedDuration = QUARTER_NOTE * 120 / currentTempo; for (int i = 0; i < 8; i++) { if (currentState == STATE_PAUSED) break; tone(BUZZER_PIN, notes[i], adjustedDuration); delay(adjustedDuration + 50); } noTone(BUZZER_PIN); currentState = STATE_IDLE; } ``` - **Array-Based Notes**: Pre-defined note sequence - **Tempo Adjustment**: Duration scales with tempo - **Pause Support**: Can be paused mid-scale - **State Management**: Proper state transitions ### **Melody Playing System** ```cpp void playMelodyArray(int melody[], int durations[], int length) { for (int i = 0; i < length; i++) { if (currentState == STATE_PAUSED) break; int adjustedDuration = durations[i] * 120 / currentTempo; if (melody[i] == 0) { // REST delay(adjustedDuration); } else { tone(BUZZER_PIN, melody[i], adjustedDuration); delay(adjustedDuration + 50); } } noTone(BUZZER_PIN); } ``` - **REST Support**: Handles silence periods (frequency = 0) - **Tempo Scaling**: All durations adjust to current tempo - **Pause Integration**: Can pause during melody playback - **Memory Efficiency**: Uses arrays for compact storage ### **Control Functions** #### **Stop Sound** ```cpp void stopSound() { noTone(BUZZER_PIN); currentState = STATE_IDLE; Serial.println("Sound stopped"); } ``` - **Immediate Stop**: noTone() stops sound instantly - **State Reset**: Returns to IDLE state - **User Feedback**: Confirms stop action #### **Pause/Resume** ```cpp void togglePause() { if (currentState == STATE_PLAYING) { currentState = STATE_PAUSED; noTone(BUZZER_PIN); Serial.println("Paused"); } else if (currentState == STATE_PAUSED) { currentState = STATE_PLAYING; Serial.println("Resumed"); } } ``` - **Toggle Logic**: Switches between PLAYING and PAUSED - **Sound Control**: Stops sound when pausing - **State Preservation**: Maintains current position #### **Tempo Control** ```cpp void increaseTempo() { currentTempo += 10; if (currentTempo > 200) currentTempo = 200; Serial.print("Tempo: "); Serial.print(currentTempo); Serial.println(" BPM"); } ``` - **Incremental Changes**: 10 BPM adjustments - **Range Limiting**: Prevents extreme tempos - **Status Display**: Shows current tempo ### **Advanced Features** #### **Random Melody Generation** ```cpp void playRandomMelody() { int notes[] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5}; int durations[] = {QUARTER_NOTE, EIGHTH_NOTE, HALF_NOTE}; for (int i = 0; i < 8; i++) { int randomNote = notes[random(8)]; int randomDuration = durations[random(3)]; // ... play note } } ``` - **Random Selection**: Uses random() function for variety - **Note Pool**: Pre-defined set of musical notes - **Duration Variety**: Mix of different note lengths - **Creative Output**: Generates unique musical sequences #### **Custom Frequency Input** ```cpp void playCustomFrequency() { Serial.println("Enter frequency (20-20000 Hz):"); while (!Serial.available()) delay(100); int frequency = Serial.parseInt(); if (frequency >= 20 && frequency <= 20000) { tone(BUZZER_PIN, frequency, 1000); delay(1050); noTone(BUZZER_PIN); } } ``` - **User Input**: Waits for frequency input - **Range Validation**: Ensures frequency is within audible range - **Direct Control**: Plays exact frequency specified - **Educational Value**: Learn about frequency relationships ### **User Interface Design** - **Simple Commands**: Single character inputs for ease of use - **Comprehensive Help**: '?' command shows all available options - **Status Display**: Shows current state and settings - **Immediate Feedback**: Confirms all user actions ## **Expected Output** ### **Serial Monitor Output** ``` TK37 Interactive Music Player ============================= Commands: 1-8: Play notes C4-B4 q-w: Play notes C5-B5 a: Play ascending scale d: Play descending scale t: Play Twinkle Twinkle h: Play Happy Birthday j: Play Jingle Bells s: Stop all sounds p: Pause/Resume l: Toggle loop mode +: Increase tempo -: Decrease tempo v: Adjust volume r: Random melody c: Custom frequency ?: Show help Current Status: State: IDLE Tempo: 120 BPM Loop: OFF > 1 Playing note: 262 Hz > a Playing ascending C major scale > + Tempo: 130 BPM > p Paused ``` ### **Audio Output** - **Individual Notes**: Clear, distinct musical notes when pressing 1-8 or q-w - **Scales**: Smooth ascending and descending musical scales - **Melodies**: Complete songs with proper timing and rhythm - **Tempo Changes**: Noticeable speed variations with +/- commands - **Random Melodies**: Varied, unpredictable musical sequences ### **Interactive Behavior** - **Immediate Response**: Commands execute instantly - **State Awareness**: System tracks current status - **Pause Functionality**: Can pause and resume music - **Tempo Control**: Real-time speed adjustment - **Error Handling**: Graceful handling of invalid input ## **Troubleshooting** ### **No response to commands:** - **Serial Connection**: Check Arduino is connected and port selected - **Baud Rate**: Ensure Serial.begin(9600) matches monitor setting - **Command Format**: Verify single character commands - **Buffer Issues**: Clear serial buffer if needed ### **Commands not working:** - **Case Sensitivity**: Commands are case-sensitive - **Timing Issues**: Wait for previous command to complete - **State Conflicts**: Check if system is in correct state - **Memory Issues**: Large melodies may cause problems ### **Audio quality issues:** - **Wiring Problems**: Check TK37 connections - **Power Supply**: Ensure stable 5V power - **Frequency Range**: Test with different frequencies - **Buzzer Quality**: Verify TK37 module functionality ### **Tempo control problems:** - **Range Limits**: Tempo is limited to 60-200 BPM - **Calculation Errors**: Check tempo adjustment formula - **Duration Issues**: Verify duration scaling works correctly - **State Conflicts**: Ensure tempo changes apply to current playback ### **Pause/Resume issues:** - **State Management**: Check currentState variable - **Timing Problems**: Pause may not work during very short notes - **Memory State**: System may lose position during pause - **Loop Conflicts**: Pause may not work properly with loop mode ## **Applications** ### **Educational** - **Music Education**: Interactive learning of musical concepts - **Programming Practice**: User interface and state management - **Real-time Systems**: Understanding interactive programming - **Audio Engineering**: Digital music control systems ### **Entertainment** - **Interactive Music**: User-controlled musical experiences - **Game Development**: Dynamic music systems - **Performance Art**: Interactive audio installations - **Musical Instruments**: Digital instrument interfaces ### **Commercial** - **Product Testing**: Interactive audio feedback systems - **User Interfaces**: Audio control systems - **Accessibility**: Audio interfaces for disabled users - **Marketing**: Interactive audio demonstrations ### **Prototyping** - **Audio Prototypes**: Quick interactive audio testing - **User Experience**: Test audio interaction patterns - **Interface Design**: Validate command structures - **System Integration**: Test audio system integration ## **Customization Ideas** ### **Add More Commands:** - **Additional Notes**: Include more octaves and sharps/flats - **New Melodies**: Add more songs and musical pieces - **Effects Commands**: Echo, vibrato, or other audio effects - **Pattern Commands**: Musical patterns and sequences ### **Enhance User Interface:** - **Multi-character Commands**: More complex command structures - **Menu System**: Hierarchical command menus - **Visual Feedback**: LED indicators for system status - **Display Integration**: LCD screen for status display ### **Add Advanced Features:** - **Recording Mode**: Record and playback user input - **Save/Load**: Store and retrieve musical compositions - **MIDI Integration**: Connect to MIDI devices - **Network Control**: Remote control via WiFi/Bluetooth ### **Improve Musical Capabilities:** - **Harmony**: Play multiple notes simultaneously - **Effects**: Reverb, echo, and other audio effects - **Instruments**: Different instrument sound simulations - **Composition Tools**: Built-in music composition features ## **Next Steps** - **Learn Advanced UI Design**: Study user interface principles - **Explore State Machines**: Understand complex state management - **Study Audio Processing**: Learn digital signal processing - **Build Complete Instruments**: Create full musical instruments - **Investigate MIDI**: Learn MIDI protocol and integration ## **Resources** - **User Interface Design**: Principles of interactive system design - **State Management**: Techniques for managing system states - **Serial Communication**: Arduino serial communication protocols - **Music Theory**: Advanced musical concepts and composition - **Audio Engineering**: Digital audio processing and control systems