Files
2024-07-20 22:09:06 +08:00

546 lines
17 KiB
C++

/**
* Arduino Library for GD5800 MP3 Module
*
* Copyright (C) 2014 James Sleeman, <http://sparks.gogo.co.nz/GD5800/index.html>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author James Sleeman, http://sparks.gogo.co.nz/
* @license MIT License
* @file
*/
// Please note, the Arduino IDE is a bit retarded, if the below define has an
// underscore other than _h, it goes mental. Wish it wouldn't mess
// wif ma files!
#ifndef GD5800Serial_h
#define GD5800Serial_h
#include <Arduino.h>
#if defined __AVR__ || defined(ESP8266)
#include <SoftwareSerial.h>
#elif defined (ESP32)
#include "ESP32SoftwareSerial.h"
#endif
#define MP3_EQ_NORMAL 0
#define MP3_EQ_POP 1
#define MP3_EQ_ROCK 2
#define MP3_EQ_JAZZ 3
#define MP3_EQ_CLASSIC 4
#define MP3_EQ_BASS 5
// Looping options, ALL, FOLDER, ONE and ONE_STOP are the
// only ones that appear to do much interesting
// ALL plays all the tracks in a repeating loop
// FOLDER plays all the tracks in the same folder in a repeating loop
// ONE plays the same track repeating
// ONE_STOP does not loop, plays the track and stops
// RAM seems to play one track and someties disables the ability to
// move to next/previous track, really weird.
#define MP3_LOOP_ALL 0
#define MP3_LOOP_FOLDER 1
#define MP3_LOOP_ONE 2
#define MP3_LOOP_RAM 3
#define MP3_STATUS_STOPPED 0
#define MP3_STATUS_PLAYING 1
#define MP3_STATUS_PAUSED 2
#define MP3_STATUS_FF 3
#define MP3_STATUS_FR 4
// The response from a status query we get is for some reason
// a bit... iffy, most of the time it is reliable, but sometimes
// instead of a playing (1) response, we get a paused (2) response
// even though it is playing. Stopped responses seem reliable.
// So to work around this when getStatus() is called we actually
// request the status this many times and only if one of them is STOPPED
// or they are all in agreement that it is playing or paused then
// we return that status. If some of them differ, we do another set
// of tests etc...
#define MP3_STATUS_CHECKS_IN_AGREEMENT 4
#define MP3_DEBUG 0
#if defined __AVR__ || defined(ESP8266)
class GD5800_Serial : public SoftwareSerial
{
public:
/** Create GD5800 object.
*
* Example, create global instance:
*
* GD5800_Serial mp3(8,9);
*
* For a 5v Arduino:
* -----------------
* * TX on GD5800 connects to D8 on the Arduino
* * RX on GD5800 connects to one end of a 1k resistor,
* other end of resistor connects to D9 on the Arduino
*
* For a 3v3 Arduino:
* -----------------
* * TX on GD5800 connects to D8 on the Arduino
* * RX on GD5800 connects to D9 on the Arduino
*
* Of course, power and ground are also required, VCC on GD5800 is 5v tolerant (but RX isn't totally, hence the resistor above).
*
* And then you can use in your setup():
*
* mp3.begin(9600)
* mp3.reset();
*
* and all the other commands :-)
*/
GD5800_Serial(short rxPin, short txPin) : SoftwareSerial(rxPin,txPin) { };
/** Start playing the current file.
*/
void play();
/** Restart the current (possibly paused) track from the
* beginning.
*
* Note that this is not an actual command the GD5800 knows
* what we do is mute, advance to the next track, pause,
* unmute, and go back to the previous track (which will
* cause it to start playing.
*
* That said, it appears to work just fine.
*
*/
void restart();
/** Pause the current file. To unpause, use play(),
* to unpause and go back to beginning of track use restart()
*/
void pause();
/** Play the next file.
*/
void next();
/** Play the previous file.
*/
void prev();
/** fast Reverse.
*/
void fastReverse();
/** fastForward
*/
void fastForward();
void playFileByIndexNumber(unsigned int fileNumber);
/** Increase the volume by 1 (volume ranges 0 to 30). */
void volumeUp();
/** Decrease the volume by 1 (volume ranges 0 to 30). */
void volumeDn();
/** Set the volume to a specific level (0 to 30).
*
* @param volumeFrom0To30 Level of volume to set from 0 to 30
*/
void setVolume(byte volumeFrom0To30);
/** Set the equalizer to one of 6 preset modes.
*
* @param equalizerMode One of the following,
*
* * MP3_EQ_NORMAL
* * MP3_EQ_POP
* * MP3_EQ_ROCK
* * MP3_EQ_JAZZ
* * MP3_EQ_CLASSIC
* * MP3_EQ_BASS
*
*/
void setEqualizer(byte equalizerMode); // EQ_NORMAL to EQ_BASS
/** Set the looping mode.
*
* @param loopMode One of the following,
*
* * MP3_LOOP_ALL - Loop through all files.
* * MP3_LOOP_FOLDER - Loop through all files in the same folder (SD Card only)
* * MP3_LOOP_ONE - Loop one file.
* * MP3_LOOP_RAM - Loop one file (uncertain how it is different to the previous!)
* * MP3_LOOP_NONE - No loop, just play one file and then stop. (aka MP3_LOOP_ONE_STOP)
*/
void setLoopMode(byte loopMode);
// Status querying commands
/** Get the status from the device.
*
* CAUTION! This is somewhat unreliable for the following reasons...
*
* 1. When playing from the on board memory (MP3_SRC_BUILTIN), STOPPED sems
* to never be returned, only PLAYING and PAUSED
* 2. Sometimes PAUSED is returned when it is PLAYING, to try and catch this
* getStatus() actually queries the module several times to ensure that
* it is really sure about what it tells us.
*
* @return One of MP3_STATUS_PAUSED, MP3_STATUS_PLAYING and MP3_STATUS_STOPPED
*/
byte getStatus();
/** Get the current volume level.
*
* @return Value between 0 and 30
*/
byte getVolume();
/** Get the equalizer mode.
*
* @return One of the following,
*
* * MP3_EQ_NORMAL
* * MP3_EQ_POP
* * MP3_EQ_ROCK
* * MP3_EQ_JAZZ
* * MP3_EQ_CLASSIC
* * MP3_EQ_BASS
*/
byte getEqualizer();
/** Get loop mode.
*
* @return One of the following,
*
* * MP3_LOOP_ALL - Loop through all files.
* * MP3_LOOP_FOLDER - Loop through all files in the same folder (SD Card only)
* * MP3_LOOP_ONE - Loop one file.
* * MP3_LOOP_RAM - Loop one file (uncertain how it is different to the previous!)
* * MP3_LOOP_NONE - No loop, just play one file and then stop. (aka MP3_LOOP_ONE_STOP)
*/
byte getLoopMode();
/** Count the number of files on the specified media.
*
* @param source One of MP3_SRC_BUILTIN and MP3_SRC_SDCARD
* @return Number of files present on that media.
*
*/
unsigned int countFiles();
/** For the currently playing (or paused, or file that would be played
* next if stopped) file, return the file's (FAT table) index number.
*
* This number can be used with playFileByIndexNumber();
*
* @param source One of MP3_SRC_BUILTIN and MP3_SRC_SDCARD
* @return Number of file.
*/
unsigned int currentFileIndexNumber();
protected:
/** Send a command to the GD5800 module,
* @param command Byte value of to send as from the datasheet.
* @param arg1 First (if any) argument byte
* @param arg2 Second (if any) argument byte
* @param responseBuffer Buffer to store a single line of response, if NULL, no response is read.
* @param buffLength Length of response buffer including NULL terminator.
*/
void sendCommand(byte command, byte arg1, byte arg2, char *responseBuffer, unsigned int bufferLength);
// Just some different versions of that for ease of use
void sendCommand(byte command);
void sendCommand(byte command, byte arg1);
void sendCommand(byte command, byte arg1, byte arg2);
/** Send a command to the GD5800 module, and get a response.
*
* For the query commands, the GD5800 generally sends an integer response
* (over the UART as 4 hexadecimal digits).
*
* @param command Byte value of to send as from the datasheet.
* @return Response from module.
*/
unsigned int sendCommandWithUnsignedIntResponse(byte command);
// This seems not that useful since there only seems to be a version 1 anway :/
size_t readBytesUntilAndIncluding(char terminator, char *buffer, size_t length, byte maxOneLineOnly = 0);
int waitUntilAvailable(unsigned long maxWaitTime = 1000);
};
#elif defined ESP32
class GD5800_Serial : public Esp32SoftwareSerial
{
public:
/** Create GD5800 object.
*
* Example, create global instance:
*
* GD5800_Serial mp3(8,9);
*
* For a 5v Arduino:
* -----------------
* * TX on GD5800 connects to D8 on the Arduino
* * RX on GD5800 connects to one end of a 1k resistor,
* other end of resistor connects to D9 on the Arduino
*
* For a 3v3 Arduino:
* -----------------
* * TX on GD5800 connects to D8 on the Arduino
* * RX on GD5800 connects to D9 on the Arduino
*
* Of course, power and ground are also required, VCC on GD5800 is 5v tolerant (but RX isn't totally, hence the resistor above).
*
* And then you can use in your setup():
*
* mp3.begin(9600)
* mp3.reset();
*
* and all the other commands :-)
*/
GD5800_Serial(short rxPin, short txPin) : Esp32SoftwareSerial(rxPin,txPin) { };
/** Start playing the current file.
*/
void play();
/** Restart the current (possibly paused) track from the
* beginning.
*
* Note that this is not an actual command the GD5800 knows
* what we do is mute, advance to the next track, pause,
* unmute, and go back to the previous track (which will
* cause it to start playing.
*
* That said, it appears to work just fine.
*
*/
void restart();
/** Pause the current file. To unpause, use play(),
* to unpause and go back to beginning of track use restart()
*/
void pause();
/** Play the next file.
*/
void next();
/** Play the previous file.
*/
void prev();
/** fast Reverse.
*/
void fastReverse();
/** fastForward
*/
void fastForward();
void playFileByIndexNumber(unsigned int fileNumber);
/** Increase the volume by 1 (volume ranges 0 to 30). */
void volumeUp();
/** Decrease the volume by 1 (volume ranges 0 to 30). */
void volumeDn();
/** Set the volume to a specific level (0 to 30).
*
* @param volumeFrom0To30 Level of volume to set from 0 to 30
*/
void setVolume(byte volumeFrom0To30);
/** Set the equalizer to one of 6 preset modes.
*
* @param equalizerMode One of the following,
*
* * MP3_EQ_NORMAL
* * MP3_EQ_POP
* * MP3_EQ_ROCK
* * MP3_EQ_JAZZ
* * MP3_EQ_CLASSIC
* * MP3_EQ_BASS
*
*/
void setEqualizer(byte equalizerMode); // EQ_NORMAL to EQ_BASS
/** Set the looping mode.
*
* @param loopMode One of the following,
*
* * MP3_LOOP_ALL - Loop through all files.
* * MP3_LOOP_FOLDER - Loop through all files in the same folder (SD Card only)
* * MP3_LOOP_ONE - Loop one file.
* * MP3_LOOP_RAM - Loop one file (uncertain how it is different to the previous!)
* * MP3_LOOP_NONE - No loop, just play one file and then stop. (aka MP3_LOOP_ONE_STOP)
*/
void setLoopMode(byte loopMode);
// Status querying commands
/** Get the status from the device.
*
* CAUTION! This is somewhat unreliable for the following reasons...
*
* 1. When playing from the on board memory (MP3_SRC_BUILTIN), STOPPED sems
* to never be returned, only PLAYING and PAUSED
* 2. Sometimes PAUSED is returned when it is PLAYING, to try and catch this
* getStatus() actually queries the module several times to ensure that
* it is really sure about what it tells us.
*
* @return One of MP3_STATUS_PAUSED, MP3_STATUS_PLAYING and MP3_STATUS_STOPPED
*/
byte getStatus();
/** Get the current volume level.
*
* @return Value between 0 and 30
*/
byte getVolume();
/** Get the equalizer mode.
*
* @return One of the following,
*
* * MP3_EQ_NORMAL
* * MP3_EQ_POP
* * MP3_EQ_ROCK
* * MP3_EQ_JAZZ
* * MP3_EQ_CLASSIC
* * MP3_EQ_BASS
*/
byte getEqualizer();
/** Get loop mode.
*
* @return One of the following,
*
* * MP3_LOOP_ALL - Loop through all files.
* * MP3_LOOP_FOLDER - Loop through all files in the same folder (SD Card only)
* * MP3_LOOP_ONE - Loop one file.
* * MP3_LOOP_RAM - Loop one file (uncertain how it is different to the previous!)
* * MP3_LOOP_NONE - No loop, just play one file and then stop. (aka MP3_LOOP_ONE_STOP)
*/
byte getLoopMode();
/** Count the number of files on the specified media.
*
* @param source One of MP3_SRC_BUILTIN and MP3_SRC_SDCARD
* @return Number of files present on that media.
*
*/
unsigned int countFiles();
/** For the currently playing (or paused, or file that would be played
* next if stopped) file, return the file's (FAT table) index number.
*
* This number can be used with playFileByIndexNumber();
*
* @param source One of MP3_SRC_BUILTIN and MP3_SRC_SDCARD
* @return Number of file.
*/
unsigned int currentFileIndexNumber();
protected:
/** Send a command to the GD5800 module,
* @param command Byte value of to send as from the datasheet.
* @param arg1 First (if any) argument byte
* @param arg2 Second (if any) argument byte
* @param responseBuffer Buffer to store a single line of response, if NULL, no response is read.
* @param buffLength Length of response buffer including NULL terminator.
*/
void sendCommand(byte command, byte arg1, byte arg2, char *responseBuffer, unsigned int bufferLength);
// Just some different versions of that for ease of use
void sendCommand(byte command);
void sendCommand(byte command, byte arg1);
void sendCommand(byte command, byte arg1, byte arg2);
/** Send a command to the GD5800 module, and get a response.
*
* For the query commands, the GD5800 generally sends an integer response
* (over the UART as 4 hexadecimal digits).
*
* @param command Byte value of to send as from the datasheet.
* @return Response from module.
*/
unsigned int sendCommandWithUnsignedIntResponse(byte command);
// This seems not that useful since there only seems to be a version 1 anway :/
size_t readBytesUntilAndIncluding(char terminator, char *buffer, size_t length, byte maxOneLineOnly = 0);
int waitUntilAvailable(unsigned long maxWaitTime = 1000);
};
#endif
#endif