Bluetooth MP3 Player with uLCD, Speaker, and SD card hardware components
Dependencies: 4DGL-uLCD-SE SDFileSystem mbed-rtos mbed
Fork of app-board-RTOS-Threads by
main.cpp@6:0c55c199443d, 2017-03-14 (annotated)
- Committer:
- pwu64
- Date:
- Tue Mar 14 16:36:23 2017 +0000
- Revision:
- 6:0c55c199443d
- Parent:
- 5:4fb2d9e36571
2nd Commit to try and publish mp3 player
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
dreschpe | 0:f6a57b843f79 | 1 | #include "mbed.h" |
dreschpe | 0:f6a57b843f79 | 2 | #include "rtos.h" |
pwu64 | 5:4fb2d9e36571 | 3 | #include "uLCD_4DGL.h" |
pwu64 | 5:4fb2d9e36571 | 4 | #include "SDFileSystem.h" |
pwu64 | 5:4fb2d9e36571 | 5 | #include "wave_player.h" |
pwu64 | 5:4fb2d9e36571 | 6 | #include <string> |
pwu64 | 5:4fb2d9e36571 | 7 | #include <vector> |
dreschpe | 0:f6a57b843f79 | 8 | |
pwu64 | 5:4fb2d9e36571 | 9 | /* Hardware Initialization */ |
pwu64 | 5:4fb2d9e36571 | 10 | Serial blue(p13,p14); |
pwu64 | 5:4fb2d9e36571 | 11 | BusOut myled(LED1,LED2,LED3,LED4); |
pwu64 | 5:4fb2d9e36571 | 12 | uLCD_4DGL uLCD(p28, p27, p30); |
pwu64 | 5:4fb2d9e36571 | 13 | AnalogOut Speaker(p18); |
pwu64 | 5:4fb2d9e36571 | 14 | SDFileSystem sd(p5, p6, p7, p8, "sd"); |
pwu64 | 5:4fb2d9e36571 | 15 | wave_player waver(&Speaker); |
pwu64 | 5:4fb2d9e36571 | 16 | FILE *wave_file; |
4180_1 | 4:79863d2ea5a0 | 17 | |
pwu64 | 5:4fb2d9e36571 | 18 | /* Mutexes to make the code thread safe */ |
pwu64 | 5:4fb2d9e36571 | 19 | Mutex lcd_mutex; |
pwu64 | 5:4fb2d9e36571 | 20 | Mutex stdio_mutex; |
pwu64 | 5:4fb2d9e36571 | 21 | Mutex time_mutex; |
pwu64 | 5:4fb2d9e36571 | 22 | Mutex inx_mutex; |
pwu64 | 5:4fb2d9e36571 | 23 | Mutex currSong_mutex; |
pwu64 | 5:4fb2d9e36571 | 24 | Mutex printed_mutex; //Basically will need to go around every if statement. May be usable as a generic mutex for everything/ |
dreschpe | 2:a69c8c5f5b03 | 25 | |
pwu64 | 5:4fb2d9e36571 | 26 | /* Variables */ |
pwu64 | 5:4fb2d9e36571 | 27 | bool Menu=1; //1: menu mode 0: play mode |
pwu64 | 5:4fb2d9e36571 | 28 | vector <string> songList; // vector of songs to index |
pwu64 | 5:4fb2d9e36571 | 29 | string currSong=""; |
pwu64 | 5:4fb2d9e36571 | 30 | int startInx=0; // starting index of songs to display on lcd |
pwu64 | 5:4fb2d9e36571 | 31 | int inxIncrement=2; //4=Full page scroll 1=single song scrolling |
pwu64 | 5:4fb2d9e36571 | 32 | int songTime; |
pwu64 | 5:4fb2d9e36571 | 33 | int numSongs; // total # of songs on sd card |
pwu64 | 5:4fb2d9e36571 | 34 | string dir = "/sd/myMusic/"; // directory of songs on SD card |
pwu64 | 5:4fb2d9e36571 | 35 | int songsPerPage=4; |
pwu64 | 5:4fb2d9e36571 | 36 | bool playing = false; |
pwu64 | 5:4fb2d9e36571 | 37 | bool printed = false; |
dreschpe | 0:f6a57b843f79 | 38 | |
dreschpe | 2:a69c8c5f5b03 | 39 | // Thread 1 |
pwu64 | 5:4fb2d9e36571 | 40 | // Reads in input from bluefruit module |
pwu64 | 5:4fb2d9e36571 | 41 | // Make sure to be using the Adafruit bluetooth app -> controller -> control pad |
dreschpe | 0:f6a57b843f79 | 42 | void thread1(void const *args) |
dreschpe | 0:f6a57b843f79 | 43 | { |
pwu64 | 5:4fb2d9e36571 | 44 | while(1) { |
pwu64 | 5:4fb2d9e36571 | 45 | char bnum = 0; // number 1-8 |
pwu64 | 5:4fb2d9e36571 | 46 | char bhit = 0; // number 0 or 1 (release or hit) |
pwu64 | 5:4fb2d9e36571 | 47 | if(blue.readable()) { |
pwu64 | 5:4fb2d9e36571 | 48 | stdio_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 49 | // Command come in as !B## |
pwu64 | 5:4fb2d9e36571 | 50 | if (blue.getc()=='!') { |
pwu64 | 5:4fb2d9e36571 | 51 | if (blue.getc()=='B') { //button data packet |
pwu64 | 5:4fb2d9e36571 | 52 | bnum = blue.getc(); //button number |
pwu64 | 5:4fb2d9e36571 | 53 | bhit = blue.getc(); //1=hit, 0=release |
pwu64 | 5:4fb2d9e36571 | 54 | if (blue.getc()==char(~('!' + 'B' + bnum + bhit))) { //checksum OK? |
pwu64 | 5:4fb2d9e36571 | 55 | myled = bnum - '0'; //current button number will appear on LEDs |
pwu64 | 5:4fb2d9e36571 | 56 | switch (bnum) { |
pwu64 | 5:4fb2d9e36571 | 57 | case '1': |
pwu64 | 5:4fb2d9e36571 | 58 | case '2': |
pwu64 | 5:4fb2d9e36571 | 59 | case '3': |
pwu64 | 5:4fb2d9e36571 | 60 | case '4': //number button 1-4 |
pwu64 | 5:4fb2d9e36571 | 61 | if(Menu) { |
pwu64 | 5:4fb2d9e36571 | 62 | /* Pressing a number button selects the corresponding |
pwu64 | 5:4fb2d9e36571 | 63 | numbered song and displays the number on the LCD */ |
pwu64 | 5:4fb2d9e36571 | 64 | currSong_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 65 | currSong = songList[startInx + bnum-'1']; |
pwu64 | 5:4fb2d9e36571 | 66 | currSong_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 67 | lcd_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 68 | uLCD.locate(0,15); |
pwu64 | 5:4fb2d9e36571 | 69 | uLCD.printf("Song selected: %d", bnum - '0'); |
pwu64 | 5:4fb2d9e36571 | 70 | lcd_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 71 | } |
pwu64 | 5:4fb2d9e36571 | 72 | break; |
pwu64 | 5:4fb2d9e36571 | 73 | case '5': //button 5 up arrow |
pwu64 | 5:4fb2d9e36571 | 74 | if (bhit=='1') { |
pwu64 | 5:4fb2d9e36571 | 75 | if(Menu) { |
pwu64 | 5:4fb2d9e36571 | 76 | /* Page up */ |
pwu64 | 5:4fb2d9e36571 | 77 | inx_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 78 | startInx -= inxIncrement; |
pwu64 | 5:4fb2d9e36571 | 79 | startInx = startInx<0?0:startInx; |
pwu64 | 5:4fb2d9e36571 | 80 | inx_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 81 | printed_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 82 | printed = false; |
pwu64 | 5:4fb2d9e36571 | 83 | printed_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 84 | } |
pwu64 | 5:4fb2d9e36571 | 85 | break; |
pwu64 | 5:4fb2d9e36571 | 86 | case '6': //button 6 down arrow |
pwu64 | 5:4fb2d9e36571 | 87 | if (bhit=='1') { |
pwu64 | 5:4fb2d9e36571 | 88 | if(Menu) { |
pwu64 | 5:4fb2d9e36571 | 89 | /* Page down */ |
pwu64 | 5:4fb2d9e36571 | 90 | inx_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 91 | startInx += inxIncrement; |
pwu64 | 5:4fb2d9e36571 | 92 | startInx = startInx>songList.size()-songsPerPage?songList.size()-songsPerPage:startInx; |
pwu64 | 5:4fb2d9e36571 | 93 | inx_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 94 | printed_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 95 | printed = false; |
pwu64 | 5:4fb2d9e36571 | 96 | printed_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 97 | } |
pwu64 | 5:4fb2d9e36571 | 98 | } |
pwu64 | 5:4fb2d9e36571 | 99 | break; |
pwu64 | 5:4fb2d9e36571 | 100 | case '7': //button 7 left arrow |
pwu64 | 5:4fb2d9e36571 | 101 | // stops the music from playing |
pwu64 | 5:4fb2d9e36571 | 102 | // does nothing if nothing is playing |
pwu64 | 5:4fb2d9e36571 | 103 | if (bhit=='1') { |
pwu64 | 5:4fb2d9e36571 | 104 | if(!Menu) { |
pwu64 | 5:4fb2d9e36571 | 105 | playing = false; |
pwu64 | 5:4fb2d9e36571 | 106 | Menu = 1; |
pwu64 | 5:4fb2d9e36571 | 107 | printed_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 108 | printed =false; |
pwu64 | 5:4fb2d9e36571 | 109 | printed_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 110 | } |
pwu64 | 5:4fb2d9e36571 | 111 | } |
pwu64 | 5:4fb2d9e36571 | 112 | break; |
pwu64 | 5:4fb2d9e36571 | 113 | case '8': //button 8 right arrow |
pwu64 | 5:4fb2d9e36571 | 114 | // change to play menu and play music |
pwu64 | 5:4fb2d9e36571 | 115 | if (bhit=='1') { |
pwu64 | 5:4fb2d9e36571 | 116 | if(Menu) { |
pwu64 | 5:4fb2d9e36571 | 117 | lcd_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 118 | uLCD.cls(); |
pwu64 | 5:4fb2d9e36571 | 119 | // Draw a playing icon |
pwu64 | 5:4fb2d9e36571 | 120 | uLCD.circle(60,40,40,GREEN); |
pwu64 | 5:4fb2d9e36571 | 121 | uLCD.triangle(45,70,45,10,95,40,GREEN); |
pwu64 | 5:4fb2d9e36571 | 122 | uLCD.locate(0,12); |
pwu64 | 5:4fb2d9e36571 | 123 | currSong_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 124 | // Display current song playing |
pwu64 | 5:4fb2d9e36571 | 125 | uLCD.printf("%s playing...",currSong.substr(0,currSong.find(".wav"))); |
pwu64 | 5:4fb2d9e36571 | 126 | currSong_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 127 | lcd_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 128 | playing =true; |
pwu64 | 5:4fb2d9e36571 | 129 | Menu = 0; |
pwu64 | 5:4fb2d9e36571 | 130 | } |
pwu64 | 5:4fb2d9e36571 | 131 | } |
pwu64 | 5:4fb2d9e36571 | 132 | break; |
pwu64 | 5:4fb2d9e36571 | 133 | default: |
pwu64 | 5:4fb2d9e36571 | 134 | break; |
pwu64 | 5:4fb2d9e36571 | 135 | } |
pwu64 | 5:4fb2d9e36571 | 136 | } |
pwu64 | 5:4fb2d9e36571 | 137 | } |
pwu64 | 5:4fb2d9e36571 | 138 | } |
pwu64 | 5:4fb2d9e36571 | 139 | stdio_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 140 | } |
pwu64 | 5:4fb2d9e36571 | 141 | } |
pwu64 | 5:4fb2d9e36571 | 142 | Thread::wait(250); // wait .25s |
dreschpe | 0:f6a57b843f79 | 143 | } |
dreschpe | 0:f6a57b843f79 | 144 | } |
dreschpe | 0:f6a57b843f79 | 145 | |
dreschpe | 2:a69c8c5f5b03 | 146 | // Thread 2 |
pwu64 | 5:4fb2d9e36571 | 147 | // Play the currSong selected from the bluetooth controller |
pwu64 | 5:4fb2d9e36571 | 148 | // playing is a bool defined in the waveplayer as an extern variable that adds |
pwu64 | 5:4fb2d9e36571 | 149 | // play/stop functionality |
dreschpe | 0:f6a57b843f79 | 150 | void thread2(void const *args) |
dreschpe | 0:f6a57b843f79 | 151 | { |
pwu64 | 5:4fb2d9e36571 | 152 | while(true) { |
pwu64 | 5:4fb2d9e36571 | 153 | if(!Menu && playing && !(currSong == "")) { |
pwu64 | 5:4fb2d9e36571 | 154 | time_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 155 | // restart songTime |
pwu64 | 5:4fb2d9e36571 | 156 | songTime=0; |
pwu64 | 5:4fb2d9e36571 | 157 | time_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 158 | string fileBuffer; |
pwu64 | 5:4fb2d9e36571 | 159 | currSong_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 160 | // concatenate the directory with the song selected |
pwu64 | 5:4fb2d9e36571 | 161 | fileBuffer = "/sd/myMusic/" + currSong; |
pwu64 | 5:4fb2d9e36571 | 162 | currSong_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 163 | // convert to const char* for the wave_file to open |
pwu64 | 5:4fb2d9e36571 | 164 | const char* songToPlay = fileBuffer.c_str(); |
pwu64 | 5:4fb2d9e36571 | 165 | wave_file = fopen(songToPlay, "r"); |
pwu64 | 5:4fb2d9e36571 | 166 | waver.play(wave_file); |
pwu64 | 5:4fb2d9e36571 | 167 | fclose(wave_file); |
pwu64 | 5:4fb2d9e36571 | 168 | playing =false; |
pwu64 | 5:4fb2d9e36571 | 169 | Menu=1; |
pwu64 | 5:4fb2d9e36571 | 170 | printed_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 171 | printed = false; |
pwu64 | 5:4fb2d9e36571 | 172 | printed_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 173 | } |
dreschpe | 2:a69c8c5f5b03 | 174 | Thread::wait(500); // wait 0.5s |
dreschpe | 0:f6a57b843f79 | 175 | } |
dreschpe | 0:f6a57b843f79 | 176 | } |
dreschpe | 0:f6a57b843f79 | 177 | |
pwu64 | 5:4fb2d9e36571 | 178 | int main() |
dreschpe | 1:1c6a9eaf55b5 | 179 | { |
pwu64 | 5:4fb2d9e36571 | 180 | uLCD.cls(); |
pwu64 | 5:4fb2d9e36571 | 181 | // Read all files on SD card into a string vector called songList |
pwu64 | 5:4fb2d9e36571 | 182 | // Make sure to lock appropriate mutexes |
pwu64 | 5:4fb2d9e36571 | 183 | DIR *dp; |
pwu64 | 5:4fb2d9e36571 | 184 | struct dirent *dirp; |
pwu64 | 5:4fb2d9e36571 | 185 | dp = opendir("/sd/myMusic"); |
pwu64 | 5:4fb2d9e36571 | 186 | numSongs = 0; |
pwu64 | 5:4fb2d9e36571 | 187 | if (dp != NULL) { |
dreschpe | 2:a69c8c5f5b03 | 188 | lcd_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 189 | stdio_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 190 | while ((dirp = readdir(dp)) != NULL) { |
pwu64 | 5:4fb2d9e36571 | 191 | songList.push_back(string(dirp->d_name)); |
pwu64 | 5:4fb2d9e36571 | 192 | //uLCD.printf("\r%s\r\n", string(dirp->d_name)); |
pwu64 | 5:4fb2d9e36571 | 193 | numSongs++; |
dreschpe | 1:1c6a9eaf55b5 | 194 | } |
dreschpe | 2:a69c8c5f5b03 | 195 | lcd_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 196 | stdio_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 197 | } else { |
pwu64 | 5:4fb2d9e36571 | 198 | uLCD.printf("Could not open directory!\n"); |
pwu64 | 5:4fb2d9e36571 | 199 | } |
pwu64 | 5:4fb2d9e36571 | 200 | closedir(dp); |
pwu64 | 5:4fb2d9e36571 | 201 | |
pwu64 | 5:4fb2d9e36571 | 202 | // Print songsPerPage # onto the LCD screen |
pwu64 | 5:4fb2d9e36571 | 203 | for (int i = 0; i < songsPerPage; i++) { |
pwu64 | 5:4fb2d9e36571 | 204 | lcd_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 205 | uLCD.printf("\r%d. %s\r\n\r\n", (i+1), songList[startInx+i].substr(0,songList[startInx + i].find(".wav"))); |
pwu64 | 5:4fb2d9e36571 | 206 | lcd_mutex.unlock(); |
dreschpe | 1:1c6a9eaf55b5 | 207 | } |
pwu64 | 5:4fb2d9e36571 | 208 | printed = true; |
pwu64 | 5:4fb2d9e36571 | 209 | // Set bluetooth baudrate |
pwu64 | 5:4fb2d9e36571 | 210 | blue.baud(9600); |
pwu64 | 5:4fb2d9e36571 | 211 | |
pwu64 | 5:4fb2d9e36571 | 212 | // Start threading |
pwu64 | 5:4fb2d9e36571 | 213 | Thread t1(thread1); // bluetooth |
pwu64 | 5:4fb2d9e36571 | 214 | Thread t2(thread2); //song |
pwu64 | 5:4fb2d9e36571 | 215 | |
pwu64 | 5:4fb2d9e36571 | 216 | songTime=0; |
dreschpe | 0:f6a57b843f79 | 217 | |
pwu64 | 5:4fb2d9e36571 | 218 | // Main thread - counts up songTime and displays the time the song has been |
pwu64 | 5:4fb2d9e36571 | 219 | // playing on LCD screen |
pwu64 | 5:4fb2d9e36571 | 220 | while(true) { // main is the next thread |
pwu64 | 5:4fb2d9e36571 | 221 | if(!Menu) { |
pwu64 | 5:4fb2d9e36571 | 222 | lcd_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 223 | stdio_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 224 | uLCD.locate(0,15); |
pwu64 | 5:4fb2d9e36571 | 225 | time_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 226 | uLCD.printf("%d:%02d",songTime/60,songTime%60); |
pwu64 | 5:4fb2d9e36571 | 227 | songTime++; |
pwu64 | 5:4fb2d9e36571 | 228 | time_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 229 | lcd_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 230 | stdio_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 231 | } else { |
pwu64 | 5:4fb2d9e36571 | 232 | printed_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 233 | if(!printed) { |
pwu64 | 5:4fb2d9e36571 | 234 | lcd_mutex.lock(); |
pwu64 | 5:4fb2d9e36571 | 235 | uLCD.cls(); |
pwu64 | 5:4fb2d9e36571 | 236 | for (int i = 0; i < songsPerPage; i++) { |
pwu64 | 5:4fb2d9e36571 | 237 | // Creates a substring out of the wave file names which takes out the .wav in the title |
pwu64 | 5:4fb2d9e36571 | 238 | uLCD.printf("\r%d. %s\r\n\r\n", (i+1), songList[startInx+i].substr(0,songList[startInx + i].find(".wav"))); |
pwu64 | 5:4fb2d9e36571 | 239 | } |
pwu64 | 5:4fb2d9e36571 | 240 | lcd_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 241 | printed =true; |
pwu64 | 5:4fb2d9e36571 | 242 | } |
pwu64 | 5:4fb2d9e36571 | 243 | printed_mutex.unlock(); |
pwu64 | 5:4fb2d9e36571 | 244 | } |
pwu64 | 5:4fb2d9e36571 | 245 | Thread::wait(1000); // Waits 1 second before updating songTime |
4180_1 | 4:79863d2ea5a0 | 246 | } |
dreschpe | 1:1c6a9eaf55b5 | 247 | } |