diff --git a/.github/workflows/build-deploy-ghpages.yml b/.github/workflows/build-deploy-ghpages.yml new file mode 100644 index 0000000..655e3ff --- /dev/null +++ b/.github/workflows/build-deploy-ghpages.yml @@ -0,0 +1,64 @@ +name: Documentation + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + id-token: write + pages: write + +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + # Checkout the repository + - name: Checkout repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: "true" + + - name: Set Version + run: echo "PROJECT_NUMBER = `git describe --tags`" >> ./docs/doxygen/doxygen-config + + - name: Build Documentation + uses: mattnotmitt/doxygen-action@v1.9.5 + with: + doxyfile-path: "./docs/doxygen/doxygen-config" + + # Upload the documentation as an artifact + - name: Upload documentation + uses: actions/upload-pages-artifact@v3.0.1 + with: + path: ./docs/html + + # Deploy job + deploy: + # Add a dependency to the build job + needs: build + + # Grant GITHUB_TOKEN the permissions required to make a Pages deployment + permissions: + pages: write # to deploy to Pages + id-token: write # to verify the deployment originates from an appropriate source + + # Deploy to the github-pages environment + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + # Specify runner + deployment step + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 # or specific "vX.X.X" version tag for this action diff --git a/.github/workflows/compile-sketch.yml b/.github/workflows/compile-sketch.yml new file mode 100644 index 0000000..c643d95 --- /dev/null +++ b/.github/workflows/compile-sketch.yml @@ -0,0 +1,125 @@ +name: Cross-compilation + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + + +jobs: + compile-sketch: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + + matrix: + board: + # Uno + # https://github.com/arduino/ArduinoCore-avr/blob/master/boards.txt + - fqbn: arduino:avr:mega + name: Arduino AVR + platforms: | + - name: arduino:avr + source-url: https://downloads.arduino.cc/packages/package_index.json + + # ESP32 + # https://github.com/espressif/arduino-esp32/blob/master/boards.txt + - fqbn: esp32:esp32:esp32thing_plus_c + name: ESP32 Thing Plus C + platforms: | + - name: esp32:esp32 + source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + + # ESP32-S2 + # https://github.com/espressif/arduino-esp32/blob/master/boards.txt + - fqbn: esp32:esp32:sparkfun_esp32s2_thing_plus + name: ESP32-S2 Thing Plus + platforms: | + - name: esp32:esp32 + source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + + # ESP32-S3 + # https://github.com/espressif/arduino-esp32/blob/master/boards.txt + - fqbn: esp32:esp32:sparkfun_esp32s3_thing_plus + name: ESP32-S3 Thing Plus + platforms: | + - name: esp32:esp32 + source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + + # ESP32-C3 + # https://github.com/espressif/arduino-esp32/blob/master/boards.txt + - fqbn: esp32:esp32:sparkfun_pro_micro_esp32c3 + name: ESP32-C3 Pro Micro + platforms: | + - name: esp32:esp32 + source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + + # ESP32-C6 + # https://github.com/espressif/arduino-esp32/blob/master/boards.txt + - fqbn: esp32:esp32:sparkfun_esp32c6_thing_plus + name: ESP32-C6 Thing Plus + platforms: | + - name: esp32:esp32 + source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + + # ESP8266 + # https://github.com/esp8266/Arduino/blob/master/boards.txt + - fqbn: esp8266:esp8266:thingdev + name: ESP8266 Thing Dev + platforms: | + - name: esp8266:esp8266 + source-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json + + # RP2040 + # https://github.com/arduino/ArduinoCore-mbed/blob/master/boards.txt + - fqbn: rp2040:rp2040:sparkfun_promicrorp2040 + name: SparkFun Pro Micro RP2040 + platforms: | + - name: rp2040:rp2040 + source-url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json + + # RP2350 + # https://github.com/arduino/ArduinoCore-mbed/blob/master/boards.txt + - fqbn: rp2040:rp2040:sparkfun_iotredboard_rp2350 + name: SparkFun IoT RedBoard RP2350 + platforms: | + - name: rp2040:rp2040 + source-url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json + + # STM32 + # https://github.com/arduino/ArduinoCore-mbed/blob/master/boards.txt + - fqbn: STMicroelectronics:stm32:GenF4 + name: STM32 GenF4 + platforms: | + - name: STMicroelectronics:stm32 + source-url: https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Branch name and details + run: | + echo "running on branch ${{ github.ref_name }}" + echo "Board: ${{matrix.board.name}}, fqbn: ${{ matrix.board.fqbn }}" + + - name: Compile Sketch + uses: arduino/compile-sketches@v1.1.0 + with: + platforms: ${{ matrix.board.platforms }} + fqbn: ${{ matrix.board.fqbn }} + libraries: | + - source-path: ./ + sketch-paths: | + - testing/Testing1_PlayFile + enable-warnings-report: true + enable-deltas-report: true + verbose: true + + # outputs: + # report-artifact-name: ${{ steps.report-artifact-name.outputs.report-artifact-name }} diff --git a/README.md b/README.md index 3af1e85..ec95893 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,9 @@ The following examples are provided with the library | Example | Description | |---|---| -|[Play File](examples/Example1_PlayFile/Example1_PlayFile.ino)| The MY1690 has a large number of features. This example presents the user with a serial menu to control the various aspects of the IC.| +|[Play File](examples/Example1_PlayFile/Example1_PlayFile.ino)| Play a single .MP3 or .WAV file from the uSD card. |[Kitchen Sink](examples/Example2_KitchenSink/Example2_KitchenSink.ino)| The MY1690 has a large number of features. This example presents the user with a serial menu to control the all aspects of the IC.| +|[Kitchen Sink ESP32](examples/Example3_KitchenSink_ESP32/Example3_KitchenSink_ESP32.ino)| Kitchen Sink example, using Hardware Serial on an ESP32 setup on pins 26 and 27.| ## License Information diff --git a/examples/Example1_PlayFile/Example1_PlayFile.ino b/examples/Example1_PlayFile/Example1_PlayFile.ino index 8224cb4..30dbfc7 100644 --- a/examples/Example1_PlayFile/Example1_PlayFile.ino +++ b/examples/Example1_PlayFile/Example1_PlayFile.ino @@ -6,9 +6,6 @@ License: MIT. See license file for more information but you can basically do whatever you want with this code. - The MY1690 has a large number of features. This example presents the user - with a serial menu to control the various aspects of the IC. - Feel like supporting our work? Buy a board from SparkFun! MY1690X Serial MP3 Player Shield: https://www.sparkfun.com/sparkfun-serial-mp3-player-shield-my1690x.html MY1690X Audio Player Breakout: https://www.sparkfun.com/sparkfun-audio-player-breakout-my1690x-16s.html @@ -34,12 +31,12 @@ SoftwareSerial serialMP3(8, 9); //RX on Arduino connected to TX on MY1690's, TX //For boards that have multiple hardware serial ports //HardwareSerial serialMP3(2); //Create serial port on ESP32: TX on 17, RX on 16 -MY1690 myMP3; +SparkFunMY1690 myMP3; void setup() { Serial.begin(115200); - Serial.println(F("MY1690 MP3 Example")); + Serial.println(F("MY1690 MP3 Example 1 - Play File")); serialMP3.begin(9600); //The MY1690 expects serial communication at 9600bps @@ -77,152 +74,17 @@ void setup() else if (playStatus == 0) Serial.println(F(" (stopped)")); - myMP3.setVolume(5); //30 is loudest. 5 is comfortable with headphones. 0 is mute. + myMP3.setVolume(15); //30 is loudest. 15 is comfortable with headphones. 0 is mute. Serial.print(F("Volume: ")); Serial.println(myMP3.getVolume()); myMP3.setPlayModeNoLoop(); - mainMenu(); + Serial.print(F("Example Complete.")); } void loop() { - if (Serial.available()) - { - byte incoming = Serial.read(); - if (incoming == 's') - { - if(myMP3.stopPlaying() == true) - Serial.println("Stop success"); - else - Serial.println("Stop command failed"); - } - else if (incoming == 'x') - { - if (myMP3.reset() == true) - Serial.println("Reset success"); - else - Serial.println("Reset command failed"); - } - else if (incoming == 'a') - { - myMP3.volumeUp(); - Serial.print("Volume: "); - Serial.println(myMP3.getVolume()); - } - else if (incoming == 'z') - { - myMP3.volumeDown(); - Serial.print("Volume: "); - Serial.println(myMP3.getVolume()); - } - else if (incoming == 'f') - { - myMP3.fastForward(); - } - else if (incoming == 'r') - { - myMP3.rewind(); - } - else if (incoming == 'p') - { - myMP3.playPause(); - } - else if (incoming == 'e') - { - int currentEQ = myMP3.getEQ(); - currentEQ++; //Go to next EQ. Device automatically wraps. - - myMP3.setEQ(currentEQ); - - currentEQ = myMP3.getEQ(); - Serial.print(F("Current EQ: ")); - Serial.println(currentEQ); - } - else if (incoming == 'm') - { - int currentMode = myMP3.getPlayMode(); - currentMode++; //Go to next mode. - - if(currentMode > 4) - currentMode = 0; - - myMP3.setPlayMode(currentMode); - - currentMode = myMP3.getPlayMode(); - Serial.print(F("Current Mode: ")); - Serial.println(currentMode); - } - else if (incoming == '<') - { - myMP3.playPrevious(); - } - else if (incoming == '>') - { - myMP3.playNext(); - } - else if (incoming == '#') - { - delay(20); - while (Serial.available()) Serial.read(); - - Serial.println(F("Track number to play: ")); - while (Serial.available() == 0) delay(1); - int value = Serial.parseInt(); - - // Note: Track must be named 0001.mp3 to myMP3.playTrackNumber(1) - myMP3.playTrackNumber(value); - } - else if (incoming == 'c') - { - Serial.print(F("Current track: ")); - Serial.println(myMP3.getTrackNumber()); - } - else if (incoming == 't') - { - Serial.print(F("Current track elapsed time (s): ")); - Serial.println(myMP3.getTrackElapsedTime()); - } - else if (incoming == 'T') - { - Serial.print(F("Current track length (s): ")); - Serial.println(myMP3.getTrackTotalTime()); - } - else if (incoming == '\r' || incoming == '\n') - { - //Ignore these - } - else - { - Serial.print(F("Unknown command: ")); - Serial.write(incoming); - Serial.println(); - mainMenu(); - } - } -} -void mainMenu() -{ - Serial.println(); - Serial.println(F("SparkFun MY1690 Menu:")); - - Serial.println(F("s) Stop play")); - Serial.println(F("x) Reset IC")); - Serial.println(F("a) Volume up")); - Serial.println(F("z) Volume down")); - Serial.println(F("f) Fast forward")); - Serial.println(F("r) Reverse")); - Serial.println(F("p) Play/Pause toggle")); - Serial.println(F("e) Set EQ")); - Serial.println(F("m) Set play mode")); - Serial.println(F("<) Play previous")); - Serial.println(F(">) Play next")); - Serial.println(F("#) Play track number")); - Serial.println(F("c) Current track number")); - Serial.println(F("t) Track elapsed time")); - Serial.println(F("T) Track total time")); - Serial.println(F("Enter command:")); -} +} \ No newline at end of file diff --git a/examples/Example2_KitchenSink/Example2_KitchenSink.ino b/examples/Example2_KitchenSink/Example2_KitchenSink.ino index 8224cb4..12f6858 100644 --- a/examples/Example2_KitchenSink/Example2_KitchenSink.ino +++ b/examples/Example2_KitchenSink/Example2_KitchenSink.ino @@ -34,12 +34,12 @@ SoftwareSerial serialMP3(8, 9); //RX on Arduino connected to TX on MY1690's, TX //For boards that have multiple hardware serial ports //HardwareSerial serialMP3(2); //Create serial port on ESP32: TX on 17, RX on 16 -MY1690 myMP3; +SparkFunMY1690 myMP3; void setup() { Serial.begin(115200); - Serial.println(F("MY1690 MP3 Example")); + Serial.println(F("MY1690 MP3 Example 2 - Kitchen Sink")); serialMP3.begin(9600); //The MY1690 expects serial communication at 9600bps @@ -77,7 +77,7 @@ void setup() else if (playStatus == 0) Serial.println(F(" (stopped)")); - myMP3.setVolume(5); //30 is loudest. 5 is comfortable with headphones. 0 is mute. + myMP3.setVolume(15); //30 is loudest. 15 is comfortable with headphones. 0 is mute. Serial.print(F("Volume: ")); Serial.println(myMP3.getVolume()); diff --git a/examples/Example3_KitchenSink_ESP32/Example3_KitchenSink_ESP32.ino b/examples/Example3_KitchenSink_ESP32/Example3_KitchenSink_ESP32.ino new file mode 100644 index 0000000..f062579 --- /dev/null +++ b/examples/Example3_KitchenSink_ESP32/Example3_KitchenSink_ESP32.ino @@ -0,0 +1,231 @@ +/* + This example demonstrates how to use the MY1690X MP3 decoder IC with an + ESP32 board (communicating over hardware serial). + + If you are using the R3 form-factor "MY1690X Serial MP3 Player Shield", with + the "SparkFun IoT Redboard - ESP32 Development Board" this example sets up + the hardware serial port on the correct pins (26 and 27). + + Play an MP3 over software serial using the MY1690X MP3 IC + By: Nathan Seidle + SparkFun Electronics + Date: December 10th, 2021 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + The MY1690 has a large number of features. This example presents the user + with a serial menu to control the various aspects of the IC. + + Feel like supporting our work? Buy a board from SparkFun! + MY1690X Serial MP3 Player Shield: https://www.sparkfun.com/sparkfun-serial-mp3-player-shield-my1690x.html + MY1690X Audio Player Breakout: https://www.sparkfun.com/sparkfun-audio-player-breakout-my1690x-16s.html + + Hardware Connections: + MY1690 Pin -> Arduino Pin + ------------------------------------- + TXO -> 26 + RXI -> 27 + VIN -> 5V + GND -> GND + + Don't forget to load some MP3s on your sdCard and plug it in too! + Note: Track must be named 0001.mp3 to myMP3.playTrackNumber(1) +*/ + +#include "SparkFun_MY1690_MP3_Library.h" // Click here to get the library: http://librarymanager/All#SparkFun_MY1690 + +//For boards that have multiple hardware serial ports +HardwareSerial serialMP3(2); //Create serial port on ESP32 using UART2 + +SparkFunMY1690 myMP3; + +void setup() +{ + Serial.begin(115200); + Serial.println(F("MY1690 MP3 Example 3 - Kitchen Sink Hardware Serial on ESP32")); + + serialMP3.begin(9600, SERIAL_8N1, 26, 27); // ESP32 HW serial arguments: (GPS_BAUD, SERIAL_8N1, RX_GPIO, TX_GPIO); + + if (myMP3.begin(serialMP3) == false) // Beginning the MP3 player requires a serial port (either hardware or software) + { + Serial.println(F("Device not detected. Check wiring. Freezing.")); + while (1); + } + + int songCount = myMP3.getSongCount(); + if (songCount == 0) + { + Serial.println(F("Oh no! No songs found. Make sure the SD card is inserted and there are MP3s on it. Freezing.")); + while (1); + } + + Serial.print(F("Number of tracks on SD card: ")); + Serial.println(songCount); + + Serial.print(F("MY1690 Version: ")); + Serial.println(myMP3.getVersion()); + + myMP3.play(); //Will play the lowest numbered song in the folder + + //It takes ~30ms for a track to start playing. If we check immediately, the track has not yet started. + delay(50); + + int playStatus = myMP3.getPlayStatus(); + // 0 = stop, 1 = play, 2 = pause, 3 = fast forward, 4 = rewind + + Serial.print(F("playStatus: ")); + Serial.print(playStatus); + if (playStatus == 1) + Serial.println(F(" (playing)")); + else if (playStatus == 0) + Serial.println(F(" (stopped)")); + + myMP3.setVolume(15); //30 is loudest. 15 is comfortable with headphones. 0 is mute. + + Serial.print(F("Volume: ")); + Serial.println(myMP3.getVolume()); + + myMP3.setPlayModeNoLoop(); + + mainMenu(); +} + +void loop() +{ + if (Serial.available()) + { + byte incoming = Serial.read(); + if (incoming == 's') + { + if(myMP3.stopPlaying() == true) + Serial.println("Stop success"); + else + Serial.println("Stop command failed"); + } + else if (incoming == 'x') + { + if (myMP3.reset() == true) + Serial.println("Reset success"); + else + Serial.println("Reset command failed"); + } + else if (incoming == 'a') + { + myMP3.volumeUp(); + Serial.print("Volume: "); + Serial.println(myMP3.getVolume()); + } + else if (incoming == 'z') + { + myMP3.volumeDown(); + Serial.print("Volume: "); + Serial.println(myMP3.getVolume()); + } + else if (incoming == 'f') + { + myMP3.fastForward(); + } + else if (incoming == 'r') + { + myMP3.rewind(); + } + else if (incoming == 'p') + { + myMP3.playPause(); + } + else if (incoming == 'e') + { + int currentEQ = myMP3.getEQ(); + currentEQ++; //Go to next EQ. Device automatically wraps. + + myMP3.setEQ(currentEQ); + + currentEQ = myMP3.getEQ(); + Serial.print(F("Current EQ: ")); + Serial.println(currentEQ); + } + else if (incoming == 'm') + { + int currentMode = myMP3.getPlayMode(); + currentMode++; //Go to next mode. + + if(currentMode > 4) + currentMode = 0; + + myMP3.setPlayMode(currentMode); + + currentMode = myMP3.getPlayMode(); + Serial.print(F("Current Mode: ")); + Serial.println(currentMode); + } + else if (incoming == '<') + { + myMP3.playPrevious(); + } + else if (incoming == '>') + { + myMP3.playNext(); + } + else if (incoming == '#') + { + delay(20); + while (Serial.available()) Serial.read(); + + Serial.println(F("Track number to play: ")); + while (Serial.available() == 0) delay(1); + int value = Serial.parseInt(); + + // Note: Track must be named 0001.mp3 to myMP3.playTrackNumber(1) + myMP3.playTrackNumber(value); + } + else if (incoming == 'c') + { + Serial.print(F("Current track: ")); + Serial.println(myMP3.getTrackNumber()); + } + else if (incoming == 't') + { + Serial.print(F("Current track elapsed time (s): ")); + Serial.println(myMP3.getTrackElapsedTime()); + } + else if (incoming == 'T') + { + Serial.print(F("Current track length (s): ")); + Serial.println(myMP3.getTrackTotalTime()); + } + else if (incoming == '\r' || incoming == '\n') + { + //Ignore these + } + else + { + Serial.print(F("Unknown command: ")); + Serial.write(incoming); + Serial.println(); + mainMenu(); + } + } +} + +void mainMenu() +{ + Serial.println(); + Serial.println(F("SparkFun MY1690 Menu:")); + + Serial.println(F("s) Stop play")); + Serial.println(F("x) Reset IC")); + Serial.println(F("a) Volume up")); + Serial.println(F("z) Volume down")); + Serial.println(F("f) Fast forward")); + Serial.println(F("r) Reverse")); + Serial.println(F("p) Play/Pause toggle")); + Serial.println(F("e) Set EQ")); + Serial.println(F("m) Set play mode")); + Serial.println(F("<) Play previous")); + Serial.println(F(">) Play next")); + Serial.println(F("#) Play track number")); + Serial.println(F("c) Current track number")); + Serial.println(F("t) Track elapsed time")); + Serial.println(F("T) Track total time")); + Serial.println(F("Enter command:")); +} diff --git a/examples/Example4_QwiicTwist/Example4_QwiicTwist.ino b/examples/Example4_QwiicTwist/Example4_QwiicTwist.ino new file mode 100644 index 0000000..296700a --- /dev/null +++ b/examples/Example4_QwiicTwist/Example4_QwiicTwist.ino @@ -0,0 +1,135 @@ +/****************************************************************************** + Example4_QwiicTwist.ino + + This example shows how to use the Audio Player Breakout with the Qwiic Twist. + Load your uSD card up with some sound files (MP3 or WAV). + Wire up the Qwiic Twist and the Mono Audio Amp. + Press the Qwiic Twist to play the selected track number. + Rotate the Qwiic Twist to change the track number. + Watch the serial monitor for feedback. + + Development environment specifics: + IDE: Arduino 2.3.6 + Hardware Platform: SparkFun Redboard Qwiic + + By: Pete Lewis, Nathan Seidle + SparkFun Electronics + Date: May 5th, 2025 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + Hardware Connections: + Use a qwiic cable to connect from the Redboard Qwiic to the Qwiic Twist. + Connect audio-in, speakers, and power to the Mono Audio Amp. + + Feel like supporting our work? Buy a board from SparkFun! + MY1690X Serial MP3 Player Shield: https://www.sparkfun.com/sparkfun-serial-mp3-player-shield-my1690x.html + MY1690X Audio Player Breakout: https://www.sparkfun.com/sparkfun-audio-player-breakout-my1690x-16s.html + + Hardware Connections: + MY1690 Pin -> Arduino Pin + ------------------------------------- + TXO -> 8 + RXI -> 9 + VIN -> 5V + GND -> GND +******************************************************************************/ + +#include +#include "SparkFun_MY1690_MP3_Library.h" // Click here to get the library: http://librarymanager/All#SparkFun_MY1690 + +//For boards that support software serial +#include "SoftwareSerial.h" +SoftwareSerial serialMP3(8, 9); //RX on Arduino connected to TX on MY1690's, TX on Arduino connected to the MY1690's RX pin + +//For boards that have multiple hardware serial ports +//HardwareSerial serialMP3(2); //Create serial port on ESP32: TX on 17, RX on 16 + +SparkFunMY1690 myMP3; + +int songCount; //Number of songs on the SD card + +#include "SparkFun_Qwiic_Twist_Arduino_Library.h" //Click here to get the library: http://librarymanager/All#SparkFun_Twist +TWIST twist; //Create instance of this object + +int user_track_number = 1; +int user_track_number_previous = 1; + +void setup() +{ + Serial.begin(115200); + Serial.println("Example 4 - Control sound playback with a Qwiic Twist"); + + Wire.begin(); + + if (twist.begin() == false) + { + Serial.println("Twist does not appear to be connected. Please check wiring. Freezing..."); + } + + // reset twist count to 1 + twist.setCount(1); + + serialMP3.begin(9600); //The MY1690 expects serial communication at 9600bps + + if (myMP3.begin(serialMP3) == false) // Beginning the MP3 player requires a serial port (either hardware or software) + { + Serial.println(F("MY1690 Device not detected. Check wiring. Freezing.")); + while (1); + } + + songCount = myMP3.getSongCount(); + if (songCount == 0) + { + Serial.println(F("Oh no! No songs found. Make sure the SD card is inserted and there are MP3s on it. Freezing.")); + while (1); + } + + Serial.print(F("Number of tracks on SD card: ")); + Serial.println(songCount); + + Serial.print(F("MY1690 Version: ")); + Serial.println(myMP3.getVersion()); + + myMP3.play(); //Will play the lowest numbered song in the folder + + myMP3.setVolume(30); //30 is loudest. 5 is comfortable with headphones. 0 is mute. + + Serial.print(F("Volume: ")); + Serial.println(myMP3.getVolume()); + + myMP3.setPlayModeNoLoop(); +} + +void loop() +{ + if (twist.isPressed()) + { + Serial.print(" Pressed! Playing track number: "); + Serial.println(user_track_number); + myMP3.playTrackNumber(user_track_number); + delay(500); + } + + user_track_number = twist.getCount(); + + if (user_track_number > songCount) // limit max track number to song count + { + user_track_number = songCount; + twist.setCount(user_track_number); // reset twist count to track number + } + else if (user_track_number < 1) // limit min track number to 1 + { + user_track_number = 1; + twist.setCount(user_track_number); // reset twist count to track number + } + + // check if the user has changed the track number + if (user_track_number != user_track_number_previous) + { + user_track_number_previous = user_track_number; + Serial.print("user_track_number: "); + Serial.println(user_track_number); + } + delay(10); +} diff --git a/keywords.txt b/keywords.txt index 2bd9d85..8d3c13a 100644 --- a/keywords.txt +++ b/keywords.txt @@ -6,7 +6,7 @@ # Datatypes (KEYWORD1) ####################################### -MY1690 KEYWORD1 +SparkFunMY1690 KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) diff --git a/library.properties b/library.properties index 183a91f..431b8d2 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun MY1690 MP3 Decoder Library -version=1.0.0 +version=1.0.1 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for the SparkFun MY1690 MP3 decoder breakout. diff --git a/src/SparkFun_MY1690_MP3_Library.cpp b/src/SparkFun_MY1690_MP3_Library.cpp index 17968a6..62e09e1 100644 --- a/src/SparkFun_MY1690_MP3_Library.cpp +++ b/src/SparkFun_MY1690_MP3_Library.cpp @@ -1,13 +1,13 @@ /*! - * @file SparkFun_MY1690_MP3_Library.cpp - * @brief This is a library written for the MY1690 Serial MP3 player + * @file SparkFun_SparkFunMY1690_MP3_Library.cpp + * @brief This is a library written for the SparkFunMY1690 Serial MP3 player * * SparkFun sells these at its website: www.sparkfun.com * * Do you like this library? Help support SparkFun. Buy a board! * https://www.sparkfun.com/products/15050 * - * https://github.com/sparkfun/SparkFun_MY1690_MP3_Decoder_Arduino_Library + * https://github.com/sparkfun/SparkFun_SparkFunMY1690_MP3_Decoder_Arduino_Library * * @author SparkFun Electronics * @date 2024 @@ -17,11 +17,11 @@ */ #include "SparkFun_MY1690_MP3_Library.h" -MY1690::MY1690() +SparkFunMY1690::SparkFunMY1690() { } -bool MY1690::begin(Stream &serialPort, uint8_t pin) +bool SparkFunMY1690::begin(Stream &serialPort, uint8_t pin) { _serialPort = &serialPort; _busyPin = pin; @@ -43,7 +43,7 @@ bool MY1690::begin(Stream &serialPort, uint8_t pin) } // Try to get the version number from the device -uint16_t MY1690::getVersion(void) +uint16_t SparkFunMY1690::getVersion(void) { commandBytes[0] = MP3_COMMAND_GET_VERSION_NUMBER; @@ -71,7 +71,7 @@ uint16_t MY1690::getVersion(void) } // Verify the device responds correctly with a version number -bool MY1690::isConnected(void) +bool SparkFunMY1690::isConnected(void) { int version = getVersion(); if (version == 100 || version == 101) @@ -81,7 +81,7 @@ bool MY1690::isConnected(void) } // Play all songs on the SD card, then loop -bool MY1690::setPlayModeFull(void) +bool SparkFunMY1690::setPlayModeFull(void) { commandBytes[0] = MP3_COMMAND_SET_LOOP_MODE; commandBytes[1] = MP3_LOOP_MODE_FULL; @@ -90,7 +90,7 @@ bool MY1690::setPlayModeFull(void) } // Play all songs in the folder, then loop -bool MY1690::setPlayModeFolder(void) +bool SparkFunMY1690::setPlayModeFolder(void) { commandBytes[0] = MP3_COMMAND_SET_LOOP_MODE; commandBytes[1] = MP3_LOOP_MODE_FOLDER; @@ -99,7 +99,7 @@ bool MY1690::setPlayModeFolder(void) } // Play song, then loop -bool MY1690::setPlayModeSingle(void) +bool SparkFunMY1690::setPlayModeSingle(void) { commandBytes[0] = MP3_COMMAND_SET_LOOP_MODE; commandBytes[1] = MP3_LOOP_MODE_SINGLE; @@ -108,7 +108,7 @@ bool MY1690::setPlayModeSingle(void) } // Play random song, then play another random song, with no end -bool MY1690::setPlayModeRandom(void) +bool SparkFunMY1690::setPlayModeRandom(void) { commandBytes[0] = MP3_COMMAND_SET_LOOP_MODE; commandBytes[1] = MP3_LOOP_MODE_RANDOM; @@ -117,7 +117,7 @@ bool MY1690::setPlayModeRandom(void) } // Play a song, then stop -bool MY1690::setPlayModeNoLoop(void) +bool SparkFunMY1690::setPlayModeNoLoop(void) { commandBytes[0] = MP3_COMMAND_SET_LOOP_MODE; commandBytes[1] = MP3_LOOP_MODE_NO_LOOP; @@ -125,7 +125,7 @@ bool MY1690::setPlayModeNoLoop(void) return (getOKResponse()); } -uint16_t MY1690::getSongCount(void) +uint16_t SparkFunMY1690::getSongCount(void) { commandBytes[0] = MP3_COMMAND_GET_SONG_COUNT; sendCommand(1); @@ -133,7 +133,7 @@ uint16_t MY1690::getSongCount(void) return (getNumberResponse()); } -uint16_t MY1690::getTrackNumber(void) +uint16_t SparkFunMY1690::getTrackNumber(void) { commandBytes[0] = MP3_COMMAND_GET_CURRENT_TRACK; sendCommand(1); @@ -141,7 +141,7 @@ uint16_t MY1690::getTrackNumber(void) return (getNumberResponse()); } -uint16_t MY1690::getTrackElapsedTime(void) +uint16_t SparkFunMY1690::getTrackElapsedTime(void) { commandBytes[0] = MP3_COMMAND_GET_CURRENT_TRACK_TIME; sendCommand(1); @@ -149,7 +149,7 @@ uint16_t MY1690::getTrackElapsedTime(void) return (getNumberResponse()); } -uint16_t MY1690::getTrackTotalTime(void) +uint16_t SparkFunMY1690::getTrackTotalTime(void) { commandBytes[0] = MP3_COMMAND_GET_CURRENT_TRACK_TIME_TOTAL; sendCommand(1); @@ -157,7 +157,7 @@ uint16_t MY1690::getTrackTotalTime(void) return (getNumberResponse()); } -bool MY1690::playTrackNumber(uint16_t trackNumber) +bool SparkFunMY1690::playTrackNumber(uint16_t trackNumber) { commandBytes[0] = MP3_COMMAND_SELECT_TRACK_PLAY; commandBytes[1] = trackNumber >> 8; // MSB @@ -166,9 +166,9 @@ bool MY1690::playTrackNumber(uint16_t trackNumber) return (getOKResponse()); } -bool MY1690::setVolume(uint8_t volumeLevel) +bool SparkFunMY1690::setVolume(uint8_t volumeLevel) { - // Any number above 30 will be automatically set to 30 by MY1690 + // Any number above 30 will be automatically set to 30 by SparkFunMY1690 // Trim value so return is true if (volumeLevel > 30) volumeLevel = 30; @@ -183,7 +183,7 @@ bool MY1690::setVolume(uint8_t volumeLevel) return (false); } -uint8_t MY1690::getVolume(void) +uint8_t SparkFunMY1690::getVolume(void) { commandBytes[0] = MP3_COMMAND_GET_VOLUME; sendCommand(1); @@ -191,27 +191,27 @@ uint8_t MY1690::getVolume(void) return (volLevel); } -bool MY1690::volumeUp(void) +bool SparkFunMY1690::volumeUp(void) { commandBytes[0] = MP3_COMMAND_VOLUME_UP; sendCommand(1); return (getOKResponse()); } -bool MY1690::volumeDown(void) +bool SparkFunMY1690::volumeDown(void) { commandBytes[0] = MP3_COMMAND_VOLUME_DOWN; sendCommand(1); return (getOKResponse()); } -uint8_t MY1690::getEQ(void) +uint8_t SparkFunMY1690::getEQ(void) { commandBytes[0] = MP3_COMMAND_GET_EQ; sendCommand(1); return (getNumberResponse()); } -bool MY1690::setEQ(uint8_t eqType) +bool SparkFunMY1690::setEQ(uint8_t eqType) { commandBytes[0] = MP3_COMMAND_SET_EQ_MODE; commandBytes[1] = eqType; @@ -219,7 +219,7 @@ bool MY1690::setEQ(uint8_t eqType) return (getOKResponse()); } -bool MY1690::setPlayMode(uint8_t playMode) +bool SparkFunMY1690::setPlayMode(uint8_t playMode) { commandBytes[0] = MP3_COMMAND_SET_LOOP_MODE; commandBytes[1] = playMode; @@ -227,14 +227,14 @@ bool MY1690::setPlayMode(uint8_t playMode) return (getOKResponse()); } -uint8_t MY1690::getPlayMode(void) +uint8_t SparkFunMY1690::getPlayMode(void) { commandBytes[0] = MP3_COMMAND_GET_LOOP_MODE; sendCommand(1); return (getNumberResponse()); } -bool MY1690::isPlaying(void) +bool SparkFunMY1690::isPlaying(void) { if (_busyPin == 255) { @@ -249,14 +249,14 @@ bool MY1690::isPlaying(void) } // Responds with '0000 \r\n' (note the space), '0001 \r\n', etc -uint8_t MY1690::getPlayStatus(void) +uint8_t SparkFunMY1690::getPlayStatus(void) { commandBytes[0] = MP3_COMMAND_GET_STATUS; sendCommand(1); return (getNumberResponse()); } -void MY1690::play(void) +void SparkFunMY1690::play(void) { commandBytes[0] = MP3_COMMAND_PLAY; sendCommand(1); @@ -265,21 +265,21 @@ void MY1690::play(void) // User can also use the isPlaying() after 30ms to see if song has started } -bool MY1690::pause(void) +bool SparkFunMY1690::pause(void) { commandBytes[0] = MP3_COMMAND_PAUSE; sendCommand(1); return (getOKResponse()); } -bool MY1690::playNext(void) +bool SparkFunMY1690::playNext(void) { commandBytes[0] = MP3_COMMAND_NEXT; sendCommand(1); return (getOKResponse()); } -bool MY1690::playPrevious(void) +bool SparkFunMY1690::playPrevious(void) { commandBytes[0] = MP3_COMMAND_PREVIOUS; sendCommand(1); @@ -288,7 +288,7 @@ bool MY1690::playPrevious(void) // Device responds with 'OK' // If a song is playing, then ~14ms later 'STOP' is reported -bool MY1690::stopPlaying(void) +bool SparkFunMY1690::stopPlaying(void) { // Use hardware pins or software command if (isPlaying() == false) @@ -308,7 +308,7 @@ bool MY1690::stopPlaying(void) return (false); } -bool MY1690::reset(void) +bool SparkFunMY1690::reset(void) { // Device responds with 'OK' // Then 'STOPMP3' ~18ms later @@ -319,7 +319,7 @@ bool MY1690::reset(void) } // Advance track ~1s -bool MY1690::fastForward(void) +bool SparkFunMY1690::fastForward(void) { commandBytes[0] = MP3_COMMAND_FASTFOWARD; sendCommand(1); @@ -327,7 +327,7 @@ bool MY1690::fastForward(void) } // Rewind track ~1s -bool MY1690::rewind(void) +bool SparkFunMY1690::rewind(void) { commandBytes[0] = MP3_COMMAND_REWIND; sendCommand(1); @@ -335,18 +335,18 @@ bool MY1690::rewind(void) } // Toggle play/pause on this track -bool MY1690::playPause(void) +bool SparkFunMY1690::playPause(void) { commandBytes[0] = MP3_COMMAND_PLAY_PAUSE; sendCommand(1); return (getOKResponse()); } -// In version 1.1, sometimes MY1690 responds with '0000 \r\n' to a get command. No OK, and a space. +// In version 1.1, sometimes SparkFunMY1690 responds with '0000 \r\n' to a get command. No OK, and a space. // Sometimes 'OK0001 \r\n'. Ok, and a space. Yay! // In version 1.0 it was lower case letters. In v1.1, it's upper case HEX. // Convert the four letters to a decimal value -uint16_t MY1690::getNumberResponse(void) +uint16_t SparkFunMY1690::getNumberResponse(void) { const uint8_t maxLength = 9; uint8_t okResponseOffset = 0; @@ -406,13 +406,13 @@ uint16_t MY1690::getNumberResponse(void) } // MY1690 responds with OK (no \n \r) in ASCII to a control command -bool MY1690::getOKResponse(void) +bool SparkFunMY1690::getOKResponse(void) { return (getStringResponse("OK")); } // Returns true if MY1690 responds with a given string -bool MY1690::getStringResponse(const char *expectedResponse) +bool SparkFunMY1690::getStringResponse(const char *expectedResponse) { uint8_t expectedLength = strlen(expectedResponse); @@ -448,7 +448,7 @@ bool MY1690::getStringResponse(const char *expectedResponse) } // Returns false if no serial data is seen after maxTimeout -bool MY1690::responseAvailable(uint8_t maxTimeout) +bool SparkFunMY1690::responseAvailable(uint8_t maxTimeout) { uint8_t counter = 0; @@ -462,7 +462,7 @@ bool MY1690::responseAvailable(uint8_t maxTimeout) return (true); } -void MY1690::clearBuffer(void) +void SparkFunMY1690::clearBuffer(void) { while (_serialPort->available()) { @@ -472,7 +472,7 @@ void MY1690::clearBuffer(void) return; } -void MY1690::sendCommand(uint8_t commandLength) +void SparkFunMY1690::sendCommand(uint8_t commandLength) { clearBuffer(); // Clear anything in the buffer diff --git a/src/SparkFun_MY1690_MP3_Library.h b/src/SparkFun_MY1690_MP3_Library.h index 471f84d..b6ad075 100644 --- a/src/SparkFun_MY1690_MP3_Library.h +++ b/src/SparkFun_MY1690_MP3_Library.h @@ -74,7 +74,7 @@ #define MP3_END_CODE 0xEF /*! - * @class MY1690 + * @class SparkFunMY1690 * @brief A library for controlling the MY1690 Serial MP3 player module. * * This class provides an interface to control the MY1690 MP3 player module @@ -83,7 +83,7 @@ * * @note The MY1690 module requires a serial connection and optionally a busy pin. */ -class MY1690 +class SparkFunMY1690 { protected: @@ -93,7 +93,7 @@ class MY1690 public: uint8_t commandBytes[MP3_NUM_CMD_BYTES]; - MY1690(); + SparkFunMY1690(); /** * @brief Initializes the MY1690 MP3 player module. diff --git a/testing/Testing1_PlayFile/Testing1_PlayFile.ino b/testing/Testing1_PlayFile/Testing1_PlayFile.ino new file mode 100644 index 0000000..e54664a --- /dev/null +++ b/testing/Testing1_PlayFile/Testing1_PlayFile.ino @@ -0,0 +1,240 @@ +/* + Play an MP3 over software serial using the MY1690X MP3 IC + By: Nathan Seidle + SparkFun Electronics + Date: December 10th, 2021 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + The MY1690 has a large number of features. This example presents the user + with a serial menu to control the various aspects of the IC. + + Feel like supporting our work? Buy a board from SparkFun! + MY1690X Serial MP3 Player Shield: https://www.sparkfun.com/sparkfun-serial-mp3-player-shield-my1690x.html + MY1690X Audio Player Breakout: https://www.sparkfun.com/sparkfun-audio-player-breakout-my1690x-16s.html + + Hardware Connections: + MY1690 Pin -> Arduino Pin + ------------------------------------- + TXO -> 8 + RXI -> 9 + VIN -> 5V + GND -> GND + + Don't forget to load some MP3s on your sdCard and plug it in too! + Note: Track must be named 0001.mp3 to myMP3.playTrackNumber(1) +*/ + +// Note: A testing version of the play all sketch - just to validate compiles + +#include "SparkFun_MY1690_MP3_Library.h" // Click here to get the library: http://librarymanager/All#SparkFun_MY1690 + +#if defined(ESP32) +// For boards that have multiple hardware serial ports +HardwareSerial serialMP3(2); // Create serial port on ESP32: TX on 17, RX on 16 + +#else +// For boards that support software serial +#include "SoftwareSerial.h" +// RX on Arduino connected to TX on MY1690's, TX on Arduino connected to the MY1690's RX pin +SoftwareSerial serialMP3(8, 9); +#endif + +SparkFunMY1690 myMP3; + +void setup() +{ + Serial.begin(115200); + Serial.println(F("MY1690 MP3 Example")); + + serialMP3.begin(9600); // The MY1690 expects serial communication at 9600bps + + if (myMP3.begin(serialMP3) == + false) // Beginning the MP3 player requires a serial port (either hardware or software) + { + Serial.println(F("Device not detected. Check wiring. Freezing.")); + while (1) + ; + } + + int songCount = myMP3.getSongCount(); + if (songCount == 0) + { + Serial.println( + F("Oh no! No songs found. Make sure the SD card is inserted and there are MP3s on it. Freezing.")); + while (1) + ; + } + + Serial.print(F("Number of tracks on SD card: ")); + Serial.println(songCount); + + Serial.print(F("MY1690 Version: ")); + Serial.println(myMP3.getVersion()); + + myMP3.play(); // Will play the lowest numbered song in the folder + + // It takes ~30ms for a track to start playing. If we check immediately, the track has not yet started. + delay(50); + + int playStatus = myMP3.getPlayStatus(); + // 0 = stop, 1 = play, 2 = pause, 3 = fast forward, 4 = rewind + + Serial.print(F("playStatus: ")); + Serial.print(playStatus); + if (playStatus == 1) + Serial.println(F(" (playing)")); + else if (playStatus == 0) + Serial.println(F(" (stopped)")); + + myMP3.setVolume(5); // 30 is loudest. 5 is comfortable with headphones. 0 is mute. + + Serial.print(F("Volume: ")); + Serial.println(myMP3.getVolume()); + + myMP3.setPlayModeNoLoop(); + + mainMenu(); +} + +void loop() +{ + if (Serial.available()) + { + byte incoming = Serial.read(); + if (incoming == 's') + { + if (myMP3.stopPlaying() == true) + Serial.println("Stop success"); + else + Serial.println("Stop command failed"); + } + else if (incoming == 'x') + { + if (myMP3.reset() == true) + Serial.println("Reset success"); + else + Serial.println("Reset command failed"); + } + else if (incoming == 'a') + { + myMP3.volumeUp(); + Serial.print("Volume: "); + Serial.println(myMP3.getVolume()); + } + else if (incoming == 'z') + { + myMP3.volumeDown(); + Serial.print("Volume: "); + Serial.println(myMP3.getVolume()); + } + else if (incoming == 'f') + { + myMP3.fastForward(); + } + else if (incoming == 'r') + { + myMP3.rewind(); + } + else if (incoming == 'p') + { + myMP3.playPause(); + } + else if (incoming == 'e') + { + int currentEQ = myMP3.getEQ(); + currentEQ++; // Go to next EQ. Device automatically wraps. + + myMP3.setEQ(currentEQ); + + currentEQ = myMP3.getEQ(); + Serial.print(F("Current EQ: ")); + Serial.println(currentEQ); + } + else if (incoming == 'm') + { + int currentMode = myMP3.getPlayMode(); + currentMode++; // Go to next mode. + + if (currentMode > 4) + currentMode = 0; + + myMP3.setPlayMode(currentMode); + + currentMode = myMP3.getPlayMode(); + Serial.print(F("Current Mode: ")); + Serial.println(currentMode); + } + else if (incoming == '<') + { + myMP3.playPrevious(); + } + else if (incoming == '>') + { + myMP3.playNext(); + } + else if (incoming == '#') + { + delay(20); + while (Serial.available()) + Serial.read(); + + Serial.println(F("Track number to play: ")); + while (Serial.available() == 0) + delay(1); + int value = Serial.parseInt(); + + // Note: Track must be named 0001.mp3 to myMP3.playTrackNumber(1) + myMP3.playTrackNumber(value); + } + else if (incoming == 'c') + { + Serial.print(F("Current track: ")); + Serial.println(myMP3.getTrackNumber()); + } + else if (incoming == 't') + { + Serial.print(F("Current track elapsed time (s): ")); + Serial.println(myMP3.getTrackElapsedTime()); + } + else if (incoming == 'T') + { + Serial.print(F("Current track length (s): ")); + Serial.println(myMP3.getTrackTotalTime()); + } + else if (incoming == '\r' || incoming == '\n') + { + // Ignore these + } + else + { + Serial.print(F("Unknown command: ")); + Serial.write(incoming); + Serial.println(); + mainMenu(); + } + } +} + +void mainMenu() +{ + Serial.println(); + Serial.println(F("SparkFun MY1690 Menu:")); + + Serial.println(F("s) Stop play")); + Serial.println(F("x) Reset IC")); + Serial.println(F("a) Volume up")); + Serial.println(F("z) Volume down")); + Serial.println(F("f) Fast forward")); + Serial.println(F("r) Reverse")); + Serial.println(F("p) Play/Pause toggle")); + Serial.println(F("e) Set EQ")); + Serial.println(F("m) Set play mode")); + Serial.println(F("<) Play previous")); + Serial.println(F(">) Play next")); + Serial.println(F("#) Play track number")); + Serial.println(F("c) Current track number")); + Serial.println(F("t) Track elapsed time")); + Serial.println(F("T) Track total time")); + Serial.println(F("Enter command:")); +}