Part 1: Wav Music Player Using Arduino Uno and SD Card
Introduction
Few days ago, I am thinking if I could create a mini karaoke using an Arduino Uno, LCD for the lyrics display, SD card for the sound files and the lyrics, and some switches for the playback functions. I tried to search through google and youtube for some resources but even the basic functionality of music playing is not available. Lyrics display with Arduino is also a challenge, so I decided to strip the lyrics display functionality for the meantime and focus first with basic music playback. So here it is.
Bill Of Materials
- Arduino Uno for the main board microcontroller.
- Keypad and LCD shield
- the LCD will be used to display the current wav file being played. It will also be used for displaying symbols of Play, Pause, Play Next, Play Previous, Volume UP, and Volume DOWN.
- the button switches functions as basic playback control.
- SD card module which will be access by SPI communication protocol.
- SD card with converted wav files. It should be
- 8 bit resolution
- 16, 000 Hz sampling rate
- mono audio channels
- unsigned 8 bit PCM format
Hardware Instruction
- Connect the LCD/Keypad shield above the Arduino Uno.
- the speaker is connected to Arduino Uno digital pin 3 and GND. I am using this speaker box for amplification of the sound. But you may also try to connect it directly to a speaker without an amplifier, sound will be very weak.
- the SD card module is connected as follows:
- SD card Chip Select or pin 1 is connected to Arduino Uno digital pin 2 by this white wire.
- SD card MOSI or pin 2 is connected to Arduino Uno digital pin 11 by this black wire.
- SD card GND or pin 3 is connected to Arduino Uno GND pin by this brown wire.
- SD card 5V or pin 4 is connected to Arduino Uno 5V pin by this red wire.
- SD card SCLK or pin 5 is connected to Arduino Uno digital pin 13 by this orange wire.
- SD card MISO or pin 7 is connected to Arduino Uno digital pin 12 by this yellow wire.
- The sound files is accessed using the Arduino SD and SPI libraries.
- The sound is played to digital pin 3 by Pulse Width Modulation (PWM) using the Arduino TMRPCM library. The default speaker output of TMRPCM is via digital pin 9. But since I am using a Keypad/LCD shield, digital pin 9 is already use as LCD enable pin. To make this work, you need to edit the pcmConfig.h under the TMRPCM library and uncomment by removing the double forward slash before the #define USE_TIMER2. This will enable digital pin 3 as speaker output.
- pressing the SELECT button functions as Play or Pause the current music
- pressing the LEFT button functions as Play Previous music
- pressing the RIGHT button functions as Play Next music
- pressing the UP button functions as Volume UP or increase the volume. The TMRPCM library allows a volume upto 7 but a volume of 4 is the maximum this setup could allow, above this is just a noise.
- pressing the DOWN button functions as Volume DOWN or decrease the volume.
Video Demonstration
Source Code
1/*
2 * PROJECT: Arduino Mini Karaoke
3 * 1. wav files and lyrics is located in SD card
4 * 2. Lyrics will be displayed to 16x2 LCD - not yet
5 * 3.
6 *
7 * LINKS:
8 * https://github.com/TMRh20/TMRpcm/wiki
9 * https://github.com/TMRh20/TMRpcm/wiki/Advanced-Features
10 * https://github.com/TMRh20/TMRpcm
11 *
12 * https://audio.online-convert.com/convert-to-wav
13 */
14#include "TMRpcm.h"
15#include "SD.h"
16#include "SPI.h"
17#include "LiquidCrystal.h"
18
19TMRpcm audio;
20LiquidCrystal lcd( 8, 9, 4, 5, 6, 7);
21File root;
22
23#define SD_CS_PIN 2
24#define SPEAKER_PIN 3
25#define TMRPCM_VOLUME 4
26
27// ####################################
28// PROTOTHREADING DEFINITIONS HERE:
29// ####################################
30unsigned long checkKeypadTaskTimer = 0;
31const unsigned long checkKeypadTaskInterval = 200;
32
33unsigned long playMusicTaskTimer = 0;
34const unsigned long playMusicTaskInterval = 100;
35
36unsigned long updateLCDTaskTimer = 0;
37const unsigned long updateLCDTaskInterval = 5000;
38
39// ####################################
40// GLOBAL VARIABLES HERE:
41// ####################################
42String wavFiles[8];
43byte wavCnt = 0;
44byte wavCurrPos = 0;
45int leftState, rightState, upState, downState, selectState = LOW;
46long lastDebounceTime = 0; // the last time the output pin was toggled
47long debounceDelay = 500; // the debounce time; increase if the output flickers
48boolean isPlaying = false;
49boolean isPaused = false;
50boolean isPlayNOW = false;
51boolean isPlayNEXT = false;
52boolean isPlayPREVIOUS = false;
53boolean isVolumeUP = false;
54boolean isVolumeDOWN = false;
55boolean isBusy = true;
56int currVolume = TMRPCM_VOLUME;
57// variables for LCD custom char
58byte bPLAY[8] = {0x00, 0x00, 0x08, 0x0C, 0x0E, 0x0C, 0x08, 0x00};
59byte bPAUSE[8] = {0x00, 0x00, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00};
60byte bNEXT[8] = {0x00, 0x00, 0x11, 0x19, 0x1D, 0x19, 0x11, 0x00};
61byte bPREVIOUS[8] = {0x00, 0x00, 0x11, 0x13, 0x17, 0x13, 0x11, 0x00};
62byte bUPVOLUME[8] = {0x02, 0x07, 0x02, 0x11, 0x11, 0x1B, 0x0E, 0x04};
63byte bDOWNVOLUME[8] = {0x00, 0x07, 0x00, 0x11, 0x11, 0x1B, 0x0E, 0x04};
64
65// ####################################
66// PROTOTYPES HERE:
67// ####################################
68void getWAVFiles(File dir, int numTabs);
69int cntChar(String str, char chr);
70char* strToChar(String str);
71String parseString(int idSeparator, char separator, String str);
72int get_key(unsigned int input);
73
74// #########################################################
75// Put code here for initializations
76// #########################################################
77void setup()
78{
79 // Initialized LCD
80 lcd.begin(16, 2);
81 lcd.createChar(0, bPLAY);
82 lcd.createChar(1, bPAUSE);
83 lcd.createChar(2, bNEXT);
84 lcd.createChar(3, bPREVIOUS);
85 lcd.createChar(4, bUPVOLUME);
86 lcd.createChar(5, bDOWNVOLUME);
87 lcd.setCursor(0,0);
88 lcd.print(" tech-to-tinker ");
89
90 // Initialized TMRPCM
91 audio.speakerPin = SPEAKER_PIN;
92 if(!SD.begin(SD_CS_PIN))
93 {
94 //Serial.println("SD fail");
95 return;
96 }
97 audio.setVolume(TMRPCM_VOLUME); // This is the maximum volume
98
99 // Get the wav files available
100 root = SD.open("/");
101 getWAVFiles(root, 0);
102 lcd.setCursor(1,1);
103 lcd.write((byte)1);
104 lcd.setCursor(3,1);
105 lcd.print(wavFiles[wavCurrPos]);
106}
107
108// #########################################################
109// Put your main code here, to run repeatedly:
110// #########################################################
111void loop() {
112 if(millis() >= checkKeypadTaskTimer + checkKeypadTaskInterval){
113 checkKeypadTaskTimer += checkKeypadTaskInterval;
114 checkKeypad();
115 }
116
117 if(millis() >= playMusicTaskTimer + playMusicTaskInterval){
118 playMusicTaskTimer += playMusicTaskInterval;
119 playMusic();
120 }
121
122 if(millis() >= updateLCDTaskTimer + updateLCDTaskInterval){
123 updateLCDTaskTimer += updateLCDTaskInterval;
124 updateLCD();
125 }
126}
127
128void updateLCD() {
129 if ( audio.isPlaying() ) {
130 if (isPaused) { // currently in paused
131 lcd.setCursor(1,1);
132 lcd.write((byte)1);
133 } else { // from pause to play
134 lcd.setCursor(1,1);
135 lcd.write((byte)0);
136 }
137 }
138}
139
140void playMusic() {
141 if (isPlayNEXT) {
142 audio.disable(); // this is necessary to prevent overrun
143 lcd.setCursor(1,1);
144 lcd.write((byte)2);
145 isPlayNEXT = false;
146 isPlaying = false;
147 isPlayNOW = true;
148 isPaused = false;
149
150 if ( wavCurrPos < (wavCnt-1) ) {
151 wavCurrPos = wavCurrPos + 1; // increase music pointer
152 } else {
153 wavCurrPos = 0; // set music pointer to first
154 }
155 }
156
157 if (isPlayPREVIOUS) {
158 audio.disable(); // this is necessary to prevent overrun
159 lcd.setCursor(1,1);
160 lcd.write((byte)3);
161 isPlayPREVIOUS = false;
162 isPlaying = false;
163 isPlayNOW = true;
164 isPaused = false;
165
166 if ( wavCurrPos == 0 ) {
167 wavCurrPos = (wavCnt-1); // set pointer to last array
168 } else {
169 wavCurrPos = wavCurrPos - 1; // decrease pointer
170 }
171 }
172
173 if ( audio.isPlaying() ) {
174
175 } else {
176 if (isPlaying) {
177 audio.disable(); // this is necessary to prevent overrun
178 isPlayNEXT = false;
179 isPlaying = false;
180 isPlayNOW = true;
181 isPaused = false;
182
183 if ( wavCurrPos < (wavCnt-1) ) {
184 wavCurrPos = wavCurrPos + 1; // increase music pointer
185 } else {
186 wavCurrPos = 0; // set music pointer to first
187 }
188 }
189 }
190
191 if (isPlayNOW) { // Select button pressed
192 isPlayNOW = false; // disable immediately to prevent playing again
193 if ( audio.isPlaying() ) {
194 // pause playing
195 audio.pause(); // pauses/unpauses playback
196 if (isPaused) { // currently in paused
197 isPaused = false;
198 } else { // from pause to play
199 isPaused = true;
200 }
201 } else {
202 // start playing
203 audio.play( strToChar( wavFiles[wavCurrPos] ) ); //plays a file
204 lcd.setCursor(3,1);
205 lcd.print(" ");
206 lcd.setCursor(3,1);
207 lcd.print(wavFiles[wavCurrPos]);
208 isPlaying = true;
209 }
210 }
211
212 if ( (isVolumeUP) && (currVolume<4) ) {
213 lcd.setCursor(1,1);
214 lcd.write((byte)4);
215 currVolume++;
216 audio.setVolume(currVolume);
217 isVolumeUP = false;
218 }
219 if ( (isVolumeDOWN) && (currVolume>-1) ) {
220 lcd.setCursor(1,1);
221 lcd.write((byte)5);
222 currVolume--;
223 audio.setVolume(currVolume);
224 isVolumeDOWN = false;
225 }
226}
227
228int adc_key_val[5] ={50, 200, 400, 600, 800 }; // Original, works after actually reading the analog0
229int NUM_KEYS = 5;
230int adc_key_in;
231int key=-1;
232
233void checkKeypad() {
234 adc_key_in = analogRead(0); // Read the value from the sensor
235 key = get_key(adc_key_in); // Convert into key press
236 switch(key) {
237 case 0: // RIGHT
238 isPlayNEXT = true;
239 break;
240 case 1: // UP
241 isVolumeUP = true;
242 break;
243 case 2: // DOWN
244 isVolumeDOWN = true;
245 break;
246 case 3: // LEFT
247 isPlayPREVIOUS = true;
248 break;
249 case 4: // SELECT
250 if (isPlayNOW) {
251 isPlayNOW = false;
252 } else {
253 isPlayNOW = true;
254 }
255 break;
256 default: // OTHERS
257 break;
258 }
259}
260
261int get_key(unsigned int input) {
262 int k;
263 for (k = 0; k < NUM_KEYS; k++) // Start counting
264 {
265 //if (input == adc_key_val[k]) // Works, but doesn't allow for deviations
266 if (input < adc_key_val[k]) // should be more reliable as it looks for a range instead of a fixed value.
267 {
268 return k; // Breaks the for loop
269 }
270 }
271 if (k >= NUM_KEYS)k = -1; // No valid key pressed
272 return k; // So break the loop and look for another keypress
273}
274
275void getWAVFiles(File dir, int numTabs) {
276 byte fileCnt = 0;
277 while (true) {
278 File entry = dir.openNextFile();
279 if (! entry) {
280 // no more files
281 break;
282 }
283 //Serial.println(entry.name());
284 String ext = parseString(cntChar(entry.name(), '.'), '.', entry.name());
285 if (ext == "WAV")
286 {
287 wavFiles[wavCnt] = entry.name();
288 wavCnt++;
289 }
290
291 entry.close();
292 }
293
294 if (wavCnt > 0) {
295 for (byte i = 0; i < wavCnt; i++) {
296 Serial.println(wavFiles[i]);
297 }
298 }
299 Serial.print("wavCnt :");
300 Serial.println(wavCnt,HEX);
301}
302
303int cntChar(String str, char chr) {
304 int n = 0;
305 for (int i = 0; i < str.length(); i++)
306 {
307 if (str[i] == chr) n++;
308 }
309 return n;
310}
311
312char* strToChar(String str) {
313 int len = str.length() + 1;
314 char* buf = new char[len];
315 strcpy(buf, str.c_str());
316 return buf;
317}
318
319String parseString(int idSeparator, char separator, String str) { // like a split JS
320 String output = "";
321 int separatorCout = 0;
322 for (int i = 0; i < str.length(); i++)
323 {
324 if ((char)str[i] == separator)
325 {
326 separatorCout++;
327 }
328 else
329 {
330 if (separatorCout == idSeparator)
331 {
332 output += (char)str[i];
333 }
334 else if (separatorCout > idSeparator)
335 {
336 break;
337 }
338 }
339 }
340 return output;
341}
Call To Action
That’s all everyone. I hope you enjoy watching this video. Please consider supporting my channel by SUBSCRIBING. Click the LIKE button and leave your comments and suggestions in the comment box below.
Thank you and have a good day. Happy tinkering.
Up next, I will try to sync the lyrics and display to LCD.
George signing out, bye.
Posts in this series
- How to Get Started with ATTiny85 in Arduino IDE
- Tutorial: How to use MFRC522 RFID module using Arduino
- SOS Flasher Using Millis Function with Enable Switch
- Tutorial: How to use DS3231 RTC in Arduino
- Tutorial: How to use 0.96 OLED - a small and cute display
- Tutorial: Getting Started with the NRF24L01 | How to use | Arduino
- Tutorial: How to use SIM800L GSM Module for Controlling Anything | Arduino
- Tutorial: How to use Keypad | Text Entry Mode | Arduino
- Tutorial: How to use 4x4 Keypad | Arduino
- Project Idea: Arduino Voltmeter
- Project Idea: Door Lock Security | Arduino
- Multitasking with Arduino | Relay Timer Controller | using millis
- Tutorial Understanding Blink Without Delay | How to millis
- Arduino Simple LCD Menu
- How to use SIM800L GSM Module using Arduino | Make or Answer Voice Calls
- Tutorial: How to Use Arduino Uno as HID | Part 2: Arduino Mouse Emulation
- Tutorial: How to Use Arduino Uno as HID | Part 1: Arduino Keyboard Emulation
- Tutorial: How to use SIM800L DTMF to Control Anything | Arduino
- Tutorial: Arduino EEPROM
- How to use SIM800L GSM Module | Arduino | Send and Receive SMS
- 16x2 LCD Menu for Arduino
- Tutorial: Arduino GPIO | How to use Arduino Pins
- MIT App Inventor for Arduino
- RC Car using L298N, HC-06, and Arduino Uno
- How to Use LCD Keypad Shield for Arduino
- How to Use Arduino Interrupts
- Project: Automatic Alcohol Dispenser
- TUTORIAL: How to use HC-SR04 Ultrasonic Sensor with Arduino
- Source Code: Astronomia Meme and Funeral Dance | melodies the Arduino way
- How to Get Started with L293D Motor Driver Shield with Arduino
- How to Get Started with L298N Motor Driver module using Arduino
- Part 2: Wav Music Player with Lyrics Using Arduino and SD Card
- Interfacing Infrared to Arduino Uno
- How to Interface Stepper Motor to Arduino Uno
- How To Play MP3 Files on Arduino from SD Card
- What is Arduino Software Serial
- How to Interface SD card to Arduino (without SD card shield)?
- Playing Melodies Using Arduino
- 8 Degrees Of Freedom (DOF) Robot Using Arduino Uno
- How to Interface PS2 Controller to Arduino Uno
- Part 3: DF Player Mini Tinkering with Arduino Nano and LCD
- How to Interface HC-06 to Arduino
- How to make a Remote Control RC car using Arduino and HC-06 bluetooth module
- Part 2: DF Player Mini Tinkering with Arduino Nano
- Part 1: DF Player Mini - a mini cheap mp3 player
No comments yet!