# 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**

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**

```
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
TinkerBlock UNO R3 Starter Kit
Dive into the world of electronics, Arduino programming, and STEM projects with the Lonely Binary TinkerBlock Series Starter Kit.
- Choosing a selection results in a full page refresh.