408 lines
13 KiB
C++
408 lines
13 KiB
C++
/**
|
|
* Arduino Library for JQ6500 MP3 Module
|
|
*
|
|
* Copyright (C) 2014 James Sleeman, <http://sparks.gogo.co.nz/jq6500/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 JQ6500Serial_h
|
|
#define JQ6500Serial_h
|
|
|
|
#include <SoftwareSerial.h>
|
|
|
|
#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
|
|
|
|
#define MP3_SRC_SDCARD 1
|
|
#define MP3_SRC_BUILTIN 4
|
|
|
|
// 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_LOOP_ONE_STOP 4
|
|
#define MP3_LOOP_NONE 4
|
|
|
|
#define MP3_STATUS_STOPPED 0
|
|
#define MP3_STATUS_PLAYING 1
|
|
#define MP3_STATUS_PAUSED 2
|
|
|
|
// 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
|
|
|
|
class JQ6500_Serial : public SoftwareSerial
|
|
{
|
|
|
|
public:
|
|
|
|
/** Create JQ6500 object.
|
|
*
|
|
* Example, create global instance:
|
|
*
|
|
* JQ6500_Serial mp3(8,9);
|
|
*
|
|
* For a 5v Arduino:
|
|
* -----------------
|
|
* * TX on JQ6500 connects to D8 on the Arduino
|
|
* * RX on JQ6500 connects to one end of a 1k resistor,
|
|
* other end of resistor connects to D9 on the Arduino
|
|
*
|
|
* For a 3v3 Arduino:
|
|
* -----------------
|
|
* * TX on JQ6500 connects to D8 on the Arduino
|
|
* * RX on JQ6500 connects to D9 on the Arduino
|
|
*
|
|
* Of course, power and ground are also required, VCC on JQ6500 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 :-)
|
|
*/
|
|
|
|
JQ6500_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 JQ6500 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();
|
|
|
|
/** Play the next folder.
|
|
*/
|
|
|
|
void nextFolder();
|
|
|
|
/** Play the previous folder.
|
|
*/
|
|
|
|
void prevFolder();
|
|
|
|
/** Play a specific file based on it's (FAT table) index number. Note that the index number
|
|
* has nothing to do with the file name (except if you uploaded/copied them to the media in
|
|
* order of file name).
|
|
*
|
|
* To sort your SD Card FAT table, search for a FAT sorting utility for your operating system
|
|
* of choice.
|
|
*/
|
|
|
|
void playFileByIndexNumber(unsigned int fileNumber);
|
|
|
|
/** Play a specific file in a specific folder based on the name of those folder and file.
|
|
*
|
|
* Only applies to SD Card.
|
|
*
|
|
* To use this function, folders must be named from 00 to 99, and the files in those folders
|
|
* must be named from 000.mp3 to 999.mp3
|
|
*
|
|
* So to play the file on the SD Card "/03/006.mp3" use mp3.playFileNumberInFolderNumber(3, 6);
|
|
*
|
|
*/
|
|
|
|
void playFileNumberInFolderNumber(unsigned int folderNumber, 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);
|
|
|
|
/** Set the source to read mp3 data from.
|
|
*
|
|
* @param source One of the following,
|
|
*
|
|
* * MP3_SRC_BUILTIN - Files read from the on-board flash memory
|
|
* * MP3_SRC_SDCARD - Files read from the SD Card (JQ6500-28P only)
|
|
*/
|
|
|
|
void setSource(byte source); // SRC_BUILTIN or SRC_SDCARD
|
|
|
|
/** Put the device to sleep.
|
|
*
|
|
* Not recommanded if you are using SD Card as for some reason
|
|
* it appears to cause the SD Card to not be recognised again
|
|
* until the device is totally powered off and on again :-/
|
|
*
|
|
*/
|
|
|
|
void sleep();
|
|
|
|
/** Reset the device (softly).
|
|
*
|
|
* It may be necessary in practice to actually power-cycle the device
|
|
* as sometimes it can get a bit confused, especially if changing
|
|
* SD Cards on-the-fly which really doesn't work too well.
|
|
*
|
|
* So if designing a PCB/circuit including JQ6500 modules it might be
|
|
* worth while to include such ability (ie, power the device through
|
|
* a MOSFET which you can turn on/off at will).
|
|
*
|
|
*/
|
|
|
|
void reset();
|
|
|
|
// 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(byte source);
|
|
|
|
/** Count the number of folders on the specified media.
|
|
*
|
|
* Note that only SD Card can have folders.
|
|
*
|
|
* @param source One of MP3_SRC_BUILTIN and MP3_SRC_SDCARD
|
|
* @return Number of folders present on that media.
|
|
*/
|
|
|
|
unsigned int countFolders(byte source);
|
|
|
|
/** 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(byte source);
|
|
|
|
/** For the currently playing or paused file, return the
|
|
* current position in seconds.
|
|
*
|
|
* @return Number of seconds into the file currently played.
|
|
*
|
|
*/
|
|
unsigned int currentFilePositionInSeconds();
|
|
|
|
/** For the currently playing or paused file, return the
|
|
* total length of the file in seconds.
|
|
*
|
|
* @return Length of audio file in seconds.
|
|
*/
|
|
|
|
unsigned int currentFileLengthInSeconds();
|
|
|
|
/** Get the name of the "current" file on the SD Card.
|
|
*
|
|
* The current file is the one that is playing, paused, or if stopped then
|
|
* could be next to play or last played, uncertain.
|
|
*
|
|
* It would be best to only consult this when playing or paused
|
|
* and you know that the SD Card is the active source.
|
|
*
|
|
* Unfortunately there is no way to query the device to find out
|
|
* which media is the active source (at least not that I know of).
|
|
*
|
|
*/
|
|
|
|
void currentFileName(char *buffer, unsigned int bufferLength);
|
|
|
|
|
|
protected:
|
|
|
|
/** Send a command to the JQ6500 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 JQ6500 module, and get a response.
|
|
*
|
|
* For the query commands, the JQ6500 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 :/
|
|
unsigned int getVersion();
|
|
|
|
size_t readBytesUntilAndIncluding(char terminator, char *buffer, size_t length, byte maxOneLineOnly = 0);
|
|
|
|
int waitUntilAvailable(unsigned long maxWaitTime = 1000);
|
|
|
|
};
|
|
|
|
#endif |