inital commit

Dependencies:   mbed wave_player mbed-rtos 4DGL-uLCD-SE SDFileSystem2 PinDetect MMA8452

Revision:
11:1a47726ac72a
Parent:
10:5bd6abd66d12
--- a/main.cpp	Sun Dec 12 23:31:10 2021 +0000
+++ b/main.cpp	Wed Dec 15 17:17:26 2021 +0000
@@ -1,362 +1,415 @@
+/**
+ * @file main.cpp
+ * @authors Christopher Rothmann (chrisrothmann@gatech.edu) & Luke Fink (lfink6@gatech.edu)
+ * @brief C++ Code to create an MP3 player from an mBED
+ * @version 1.0
+ * @date 2021-12-13
+ * 
+ * @copyright Copyright (c) 2021
+**/
+
+
+// Define included libraries; all libraries below must be compiled together
+// Note: Some libraries have been updated to work with this code. Ensure all libraries 
+// are the correct by using those included in this github
 #include "mbed.h"
 #include "rtos.h"
 #include "SDFileSystem.h"
 #include "uLCD_4DGL.h"
 #include "wave_player.h"
-#include "LSM9DS1.h"
+#include "MMA8452.h"
 #include "PinDetect.h"
 #include <string>
 #include <vector>
 
-DigitalOut myled(LED1);
-RawSerial blue(p28,p27);
+// Defining mBED inputs & outputs
 
-SDFileSystem sd(p5, p6, p7, p8, "sd"); //SD card
-uLCD_4DGL uLCD(p13,p14,p11);
-
-LSM9DS1 imu(p9, p10, 0xD6, 0x3C);
-Serial pc(USBTX, USBRX);
+// mBED LED Outputs for Audiovisualizer/Testing & Diagnostics
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+DigitalOut led3(LED3);
+DigitalOut led4(LED4);
 
-PinDetect playpause(p23);
-PinDetect menu(p26);
-PinDetect skip(p25);
-PinDetect back(p30);
-Mutex LCD;
-Mutex Speaker;
-Mutex avgs;
-Mutex mentex;
+// Pushbuttons for MP3 Player Controls
+PinDetect prev(p21);
+PinDetect next(p22);
+PinDetect shuffle(p23);
+PinDetect play(p24);
 
+// Serial & Analog Inputs & Ouputs for Data Communication
+RawSerial blueTooth(p28,p27);
+Serial pc(USBTX, USBRX);
+SDFileSystem sd(p5, p6, p7, p12, "sd");
+uLCD_4DGL uLCD(p13,p14,p11);
+MMA8452 acc(p9, p10, 100000);
 AnalogOut DACout(p18);
+wave_player waver(&DACout);
 
-wave_player waver(&DACout);
+
+// Defining Internal Global Variables
 bool playing = false;
-bool play = 1;
-bool menu1 = 0;
-bool inmenu=0;
-int currentsong = 0;
-int avgIMU;
-int avgMic;
-int songcount;
-vector <string> songList; // vector of songs to index
-string dir = "/sd/myMusic/";
+int currentSong = 0;
+int songCount = 0;
+vector<string> songList;
+unsigned short max_range = 0xFFFF;
+
+// Defining Functions
 
-class microphone
-{
-public :
-    microphone(PinName pin);
-    float read();
-    operator float ();
-private :
-    AnalogIn _pin;
-};
-microphone::microphone (PinName pin):
-    _pin(pin)
-{
-}
-float microphone::read()
-{
-    return _pin.read();
-}
-inline microphone::operator float ()
-{
-    return _pin.read();
-}
- 
-microphone mymicrophone(p16);
-void LCDThread(void const *argument)
+/**
+ * @brief Increments integer variable currentSong by one, while circling back to first song at end of list
+ * @details Function is called both when "next song" pushbutton pressed or bluetooth command is sent;
+ * LED1 switches value when called for diagnostics & testing
+**/
+void nextSong()
 {
-    while(1){
-        Speaker.lock();
-        if(!menu1)
-        {
-            Speaker.unlock();
-            LCD.lock();
-            uLCD.cls();
-            uLCD.locate(1,1);
-            uLCD.printf("%s",songList[currentsong].substr(0,songList[currentsong].find(".wav")));
-            if(play)
-            {
-                //play
-        
-                uLCD.filled_rectangle(0,118,280,40,BLACK);
-                uLCD.triangle(120, 100, 40, 40, 10, 100, 0x0000FF);
-            }
-            else
-            {
-        //pause
-                uLCD.filled_rectangle(0,118,110,40,WHITE);
-                uLCD.filled_rectangle(50,118,100,40,BLACK);
-                uLCD.filled_rectangle(180,118,280,40,WHITE);
-            }
-            LCD.unlock();
-        }
-        else
-        {
-       
-            Speaker.unlock();
-            mentex.lock();
-            LCD.lock();
-            uLCD.cls();
-            LCD.unlock();
-            for(int i=-1; i<2; i++)
-            {
-                //add code to display and scroll through menu here 
-            
-                mentex.lock();
-                if(currentsong+i>=0)
-                {
-                  if(currentsong+i<songcount)
-                  {
-                    LCD.lock();
-                    uLCD.printf("%s\r\n\r\n", songList[currentsong+i].substr(0,songList[currentsong + i].find(".wav")));
-                    LCD.unlock();
-                  }
-                    else
-                    {
-                    LCD.lock();
-                    uLCD.printf("%s\r\n\r\n", songList[0].substr(0,songList[0].find(".wav")));
-                    LCD.unlock();
-                    }
-                }
-                else if(currentsong+i<0)
-                {
-                LCD.lock();
-                uLCD.printf("%s\r\n\r\n", songList[songcount-1].substr(0,songList[songcount-1].find(".wav")));
-                LCD.unlock();
-                }
-                
-           }
-           mentex.unlock();
-           LCD.lock();
-           uLCD.locate(2,3);
-           uLCD.printf("%s", "^^^");
-           LCD.unlock();
-        }   
-    Thread::wait(500);
-    }
-}
-void buttonThread()
-{
-    //add playpause and skip features here 
-    Speaker.lock();
-    if(!menu1)
+    //led1 = !led1;
+    if (currentSong == songCount - 1)
     {
-    playing=!playing;
-    play=!playing;
+        currentSong = 0;
     }
     else
     {
-     playing=!playing;
-     Thread::wait(500);
-     playing=!playing;   
+        currentSong++;
     }
-    myled=play;
-    Speaker.unlock();
-    Thread::wait(10);
 }
-void skipThread()
+
+/**
+ * @brief Increments integer variable currentSong by minus one, while circling back to last song at zero
+ * @details Function is called both when "previous song" pushbutton pressed or bluetooth command is sent;
+ * LED2 switches value when called for diagnostics & testing
+**/
+void prevSong()
 {
-    //add skip features
-    
-    Speaker.lock();
-    if(currentsong<songcount-1)
+    //led2 = !led2;
+    if (currentSong == 0)
     {
-    currentsong++;
+        currentSong = songCount - 1;
     }
     else
     {
-        currentsong=0;
-        }
-        if(!menu1)
-        {
-        playing=true;
-        play=!playing;
-        }
-        Speaker.unlock();
-}
-void backThread()
-{
-    Speaker.lock();
-    if(currentsong!=0)
-    {
-        currentsong--;
+        currentSong--;
     }
-    else
-    {
-       currentsong=songcount-1; 
-    }
-        if(!menu1)
-        {
-        playing=true;
-        play=!playing;
-        }
-        Speaker.unlock();
 }
-void menuThread()
-{
-    menu1=!menu1;
-}
-void BlueThread(void const *argument)
-{
-    //add bluetooth control code here 
-    char bnum =0;
-    char bhit=0;
-    while(1)
-    {
+
 
-        if(blue.readable()&&blue.writeable())
-        {
-            if (blue.getc()=='!') {
-            if (blue.getc()=='B') { //button data
-                bnum = blue.getc(); //button number
-                bhit=blue.getc();
-                if ((bnum>='1')&&(bnum<='4')) //is a number button 1..4
-                {
-                                        LCD.lock();
-        uLCD.printf("%s", "test lol");
-        LCD.unlock();
-                    switch (bnum)
-                    {
-                        case '1': //number button 1   //add playpause and skip features here 
-                        if (bhit=='1') {
-                            Speaker.lock();
-                            if(play)
-                            {
-                                playing = false;
-                            }
-                            else
-                            {
-                                playing = true;
-                            }
-                            play=!play;
-                            myled=play;
-                            Speaker.unlock();
-                            Thread::wait(10);
-                            }
-                            break;
-                        case '2': //number button 2
-                        if (bhit=='1') {
-                                mentex.lock();
-                                if(currentsong<songcount-1)
-                                {
-                                currentsong++;
-                                }
-                                else
-                                {
-                                    currentsong=0;
-                                }
-                                    if(!menu)
-                                    {
-                                    playing=true;
-                                    play=true;
-                                    }
-                                    mentex.unlock();
-                                }
-                            break;
-                        case '3': //number button 3
-                        if (bhit=='1') {
-                                mentex.lock();
-                                if(currentsong!=0)
-                                {
-                                    currentsong--;
-                                }
-                                else
-                                {
-                                   currentsong=songcount-1; 
-                                }
-                                    if(!menu)
-                                    {
-                                    playing=true;
-                                    play=true;
-                                    }
-                                    mentex.unlock();
-                                }
-                            break;
-                        case '4': //number button 4
-                        if (bhit=='1') {
-                                   mentex.lock();
-                                    menu1=!menu1;
-                                    mentex.unlock();
-                                    }
-                            break;
-                        default:
-                            break;
-                        }
-                        }
-                
-            }
-        }
-        }
-        Thread::wait(1000);
-        }
-}
-void IMUThread(void const *argument)
+/**
+ * @brief Switches boolean variable playing
+ * @details Function is called both when "pause/play" pushbutton pressed or bluetooth command is sent;
+ * LED3 switches value when called for diagnostics & testing
+**/
+void playSong()
 {
-    while(1){
-    avgs.lock();
-    //put imu averaging and next track selection code here if that is selected 
-    avgMic = int(((abs((mymicrophone - (0.67/3.3)))*500.0)+avgMic)/2);
-    imu.readAccel();
-    avgIMU=int((((imu.ax+imu.az+imu.ay)/3.0)+avgIMU)/2);
-    avgs.unlock();
-    Thread::wait(5000);
-    }
-    
+    //led3 = !led3;
+    playing = !playing;
 }
 
-int main()
+/**
+ * @brief Generates random integer within song list range to assign integer variable currentSong
+ * @details Function is called both when "shuffle song" pushbutton pressed or bluetooth command is sent;
+ * function seeds a true random value through the noise present on the 5th decimal place of an
+ * accelerometer's input values;
+ * LED4 switches value when called for diagnostics & testing
+**/
+void shuffleSong()
 {
+    //led4 = !led4;
+    double x, y, z;
+    acc.readXYZGravity(&x,&y,&z);
+    currentSong = int(100000 * (x + y + z)) % songCount;
+}
+
+// Defining Threads
+
+/**
+ * @brief Updates LCD screen according to user input & selections
+ * @details First configures LCD screen layout & songlist, then continously checks for changes in global variables
+ * integer currentSong & boolean playing to update LCD screen accordingly. No updates made if no changes found.
+ * All LCD communications occur strictly in this thread.
+ * @param *arguments Input arguments to thread used for RTOS thread library. Not needed to understand thread code.
+ */
+void LCDThread(void const *argument)
+{
+    // Configure LCD screen
     uLCD.cls();
     uLCD.baudrate(3000000);
     uLCD.background_color(BLACK);
+    uLCD.color(WHITE);
     uLCD.text_width(1);
-    uLCD.text_height(1);
-    imu.begin();
-    if (!imu.begin()) {
-        //set fail flag for imu here
+    uLCD.text_height(1);   
+
+    // Print Song List to LCD Screen
+    uLCD.locate(0,0);
+    uLCD.printf("Song List: ");
+    uLCD.locate(0,1);
+    uLCD.printf("->");
+    for(int i = 0; i < songCount; i++)
+    {
+        uLCD.locate(3,i+1);
+        uLCD.printf("%s\n\r", songList[i].substr(0,songList[i].find(".wav")));
+    }
+    
+    // Print "NOW PLAYING: " & "STATUS: " feature; initialize to first song on SD card & paused
+    uLCD.locate(0,12);
+    uLCD.printf("NOW PLAYING:");
+    uLCD.locate(0,13);
+    uLCD.printf("%s", songList[currentSong].substr(0,songList[currentSong].find(".wav")));
+    uLCD.locate(0,14);
+    uLCD.printf("STATUS: PAUSED");
+
+    // Initialize internal thread variables to check for changes to external global variables
+    bool prevPlayLCD = false;
+    int previousSongLCD = 0;
+
+    // Thread while loop to continously check for changes and update screen accordingly
+    while (true)
+    {   
+        // Check if new song has been selected
+        if (previousSongLCD != currentSong)
+        {
+            // Update "NOW PLAYING: " feature
+            uLCD.locate(0,12);
+            uLCD.printf("NOW PLAYING:");
+            uLCD.locate(0,13);
+            uLCD.printf("%s   ", songList[currentSong].substr(0,songList[currentSong].find(".wav")));
+            // Update "->" feature
+            uLCD.locate(0, previousSongLCD + 1);
+            uLCD.printf("  ");
+            uLCD.locate(0, currentSong + 1);
+            uLCD.printf("->");
+            // Set internal change check to currentSong
+            previousSongLCD = currentSong;
+        }
+        //Check if change to play/pause status
+        if (prevPlayLCD != playing)
+        {
+            // Update "STATUS: " feature
+            uLCD.locate(0,14);
+            if (playing)
+            {
+                uLCD.printf("STATUS: PLAYING");
+            }
+            else
+            {
+                uLCD.printf("STATUS: PAUSED ");
+            }
+            // Set internal change check to playing
+            prevPlayLCD = playing;
+        }
+        Thread::wait(50);
     }
-    imu.calibrate();
-    blue.baud(9600);
+}
+
+/**
+ * @brief Updates phone screen to latest currentSong playing, sends phone commands to mBED, all over BlueTooth
+ * @details See commenting in thread for step-by-step approach
+ * All BlueTooth communications occur strictly in this thread
+ * BlueTooth Control Pad Module Controls:  1 = Pause/Play, 2 = Next Song, 3 = Previous Song, 4 = Shuffle Song
+ * @param *arguments Input arguments to thread used for RTOS thread library. Not needed to understand thread code.
+ */
+void BluetoothThread(void const *argument)
+{
+    // Initialize internal thread variable to check for changes to external global variables
+    int previousSongBLE = 0;
+    // Thread while look to continously check for BlueTooth commands and update currentSong on phone
+    while (true)
+    {
+        // Update currentSong on phone
+        if (blueTooth.writeable())
+        {
+            // Check if new song has been selected
+            if (previousSongBLE != currentSong)
+            {
+                // Send currentSong name over BlueTooth
+                string str = "Current Song: ";
+                for (int i = 0; i < 14; i++)
+                {
+                    blueTooth.putc(str[i]);
+                }
+                for (int i = 0; i < songList[currentSong].size() - 4; i++)
+                {
+                    blueTooth.putc(songList[currentSong][i]);
+                }
+                blueTooth.putc('\n');
+                previousSongBLE = currentSong;
+            }
+            
+        }
+        // Read in commands from BlueTooth module
+        if (blueTooth.readable())
+        {
+            // Check for '!B' to be compatible with "Control Pad" Module serial output
+            if (blueTooth.getc()=='!')
+            {
+                if (blueTooth.getc()=='B')
+                {
+                    // Check which command was hit
+                    char bnum = blueTooth.getc();
+                    // Ensure mBED only updates on release, not hit
+                    char bhit = blueTooth.getc();
+                    if (bhit == '0')
+                    {
+                        switch (bnum)
+                            {
+                                case '1':
+                                playSong();
+                                break;
+                                
+                                case '2':
+                                nextSong();
+                                break;
+                                
+                                case '3':
+                                prevSong();
+                                break;
+                                
+                                case '4':
+                                shuffleSong();
+                                break;
+                                
+                                default:
+                                break;
+                            }
+                    }
+                }
+            }
+        }
+        Thread::wait(50);
+    }
+}
+
+/**
+ * @brief Updates Mbed LEDs to show current volume level 
+ * @details Read and scales analogOut level, then sets leds to show the level in 4 tiers. 
+ * @param *arguments Input arguments to thread used for RTOS thread library. Not needed to understand thread code.
+ */
+void AudioVisualizerThread(void const *argument)
+{
+        while(1)
+        {
+            if(playing)
+            {
+                float level = (DACout.read() - 0.25f) * 3.3f;
+                if(level<0.825)
+                {
+                    led1=true;
+                    led2=led3=led4=false;
+                }
+                else if(level>0.825&&level<1.65)
+                {
+                    led1=led2=true;
+                    led3=led4=false;
+                }
+                else if(level>1.65&&level<2.47)
+                {
+                    led1=led2=led3=true;
+                    led4=false;
+                }
+                else if(level>2.47)
+                {
+                    led1=led2=led3=led4=true;
+                }
+                Thread::wait(50);
+            }
+        }
+}
+
+// Button Interupt Functions
+
+/**
+ * @brief runs nextSong() function on pushbotton hit. Attached using PinDetect.
+**/
+void nextInt()
+{
+    nextSong();
+}
+
+/**
+ * @brief runs prevSong() function on pushbotton hit. Attached using PinDetect.
+**/
+void prevInt()
+{
+    prevSong();
+}
+
+/**
+ * @brief runs playSong() function on pushbotton hit. Attached using PinDetect.
+**/
+void playInt()
+{
+    playSong();
+}
+
+/**
+ * @brief runs shuffleSong() function on pushbotton hit. Attached using PinDetect.
+**/
+void shuffleInt()
+{
+    shuffleSong();
+}
+
+/**
+ * @brief Program main routine.
+ * @return int No return expected.
+ */
+int main()
+{   
+    // Attach & configure interupts to pushbuttons
+    next.mode(PullUp);
+    prev.mode(PullUp);
+    play.mode(PullUp);
+    shuffle.mode(PullUp);
+    next.attach_deasserted(&nextInt);
+    prev.attach_deasserted(&prevInt);
+    play.attach_deasserted(&playInt);
+    shuffle.attach_deasserted(&shuffleInt);
+    next.setSampleFrequency();
+    prev.setSampleFrequency();
+    play.setSampleFrequency();
+    shuffle.setSampleFrequency();
+    // Wait 10 milliseconds to ensure functions are attached
+    Thread::wait(10);
     
+    // Extract file list from SD Card, place in vector<string> songList
     DIR *dp;
     struct dirent *dirp;
     dp = opendir("/sd/myMusic");
-    songcount = 0;
     if(dp !=NULL)
     {
        while ((dirp = readdir(dp)) != NULL) {
             songList.push_back(string(dirp->d_name));
-            //uLCD.printf("\r%s\r\n", string(dirp->d_name));
-            songcount++;
+            songCount++;
         }
     }
-    playpause.mode(PullUp);
-    playpause.attach_deasserted(&buttonThread);
-    playpause.setSampleFrequency();
+    // Wait 10 miliseconds to ensure SD card communication complete
+    Thread::wait(10);
     
-    menu.mode(PullUp);
-    menu.attach_deasserted(&menuThread);
-    menu.setSampleFrequency();   
-    skip.mode(PullUp);
-    skip.attach_deasserted(&skipThread);
-    skip.setSampleFrequency();
-    back.mode(PullUp);
-    back.attach_deasserted(&backThread);
-    back.setSampleFrequency();
-    //LCD, Player, button Interrupt, bluetooth, imu+mic
+    // Start LCD & BlueTooth Thread
     Thread thread1(LCDThread);
-    Thread thread4(BlueThread);
-    Thread thread5(IMUThread);
-    while(1){
-    FILE *wave_file;
-    Thread::wait(1000);
-    mentex.lock();
-    string selectedSong= "/sd/myMusic/" + songList[currentsong];
-    mentex.unlock();
-    const char* song = selectedSong.c_str();
-    wave_file=fopen(song,"r");
-    if(wave_file==NULL) uLCD.printf("file open error!\n\n\r");
-    waver.play(wave_file);
-    fclose(wave_file);
+    Thread thread2(BluetoothThread);
+    Thread thread3(AudioVisualizerThread);
+
+    // Main while loop:
+    // Main loop is now considered the Speaker Thread, playing/pausing current song 
+    // based on changes in global varaibles boolean playing & integer currentSong
+    while (true)
+    {
+        // Read in selected file
+        FILE *wave_file;
+        string selectedSong= "/sd/myMusic/" + songList[currentSong];
+        const char* song = selectedSong.c_str();
+        wave_file=fopen(song,"r");
+        if(wave_file==NULL)
+        {
+            uLCD.locate(0,12);
+            uLCD.printf("file open error!");
+        }
+        // Wait 10 miliseconds to ensure file properly loaded
+        Thread::wait(10);
+        // Play file; stop/play feature built into waver library
+        waver.play(wave_file);
+        // Close file
+        fclose(wave_file);
+        // Reset playing variable so song does not repeat
+        playing = false;
     }
 }
\ No newline at end of file