/** * Arduino Library for JQ6500 MP3 Module * * Copyright (C) 2014 James Sleeman, * * 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 #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