This repository has been archived on 2024-08-19. You can view files and clone it, but cannot push or open issues or pull requests.
Arduino/JQ6500_Serial-master/JQ6500_Serial.cpp
2023-07-03 13:45:36 +03:00

335 lines
9.2 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
*/
#include <Arduino.h>
#include "JQ6500_Serial.h"
#include <SoftwareSerial.h>
void JQ6500_Serial::play()
{
this->sendCommand(0x0D);
}
void JQ6500_Serial::restart()
{
byte oldVolume = this->getVolume();
this->setVolume(0);
this->next();
this->pause();
this->setVolume(oldVolume);
this->prev();
}
void JQ6500_Serial::pause()
{
this->sendCommand(0x0E);
}
void JQ6500_Serial::next()
{
this->sendCommand(0x01);
}
void JQ6500_Serial::prev()
{
this->sendCommand(0x02);
}
void JQ6500_Serial::playFileByIndexNumber(unsigned int fileNumber)
{
this->sendCommand(0x03, (fileNumber>>8) & 0xFF, fileNumber & (byte)0xFF);
}
void JQ6500_Serial::nextFolder()
{
this->sendCommand(0x0F, 0x01);
}
void JQ6500_Serial::prevFolder()
{
this->sendCommand(0x0F, 0x00);
}
void JQ6500_Serial::playFileNumberInFolderNumber(unsigned int folderNumber, unsigned int fileNumber)
{
this->sendCommand(0x12, folderNumber & 0xFF, fileNumber & 0xFF);
}
void JQ6500_Serial::volumeUp()
{
this->sendCommand(0x04);
}
void JQ6500_Serial::volumeDn()
{
this->sendCommand(0x05);
}
void JQ6500_Serial::setVolume(byte volumeFrom0To30)
{
this->sendCommand(0x06, volumeFrom0To30);
}
void JQ6500_Serial::setEqualizer(byte equalizerMode)
{
this->sendCommand(0x07, equalizerMode);
}
void JQ6500_Serial::setLoopMode(byte loopMode)
{
this->sendCommand(0x11, loopMode);
}
void JQ6500_Serial::setSource(byte source)
{
this->sendCommand(0x09, source);
}
void JQ6500_Serial::sleep()
{
this->sendCommand(0x0A);
}
void JQ6500_Serial::reset()
{
this->sendCommand(0x0C);
delay(500); // We need some time for the reset to happen
}
byte JQ6500_Serial::getStatus()
{
byte statTotal = 0;
byte stat = 0;
do
{
statTotal = 0;
for(byte x = 0; x < MP3_STATUS_CHECKS_IN_AGREEMENT; x++)
{
stat = this->sendCommandWithUnsignedIntResponse(0x42);
if(stat == 0) return 0; // STOP is fairly reliable
statTotal += stat;
}
} while (statTotal != 1 * MP3_STATUS_CHECKS_IN_AGREEMENT && statTotal != 2 * MP3_STATUS_CHECKS_IN_AGREEMENT);
return statTotal / MP3_STATUS_CHECKS_IN_AGREEMENT;
}
byte JQ6500_Serial::getVolume() { return this->sendCommandWithUnsignedIntResponse(0x43); }
byte JQ6500_Serial::getEqualizer() { return this->sendCommandWithUnsignedIntResponse(0x44); }
byte JQ6500_Serial::getLoopMode() { return this->sendCommandWithUnsignedIntResponse(0x45); }
unsigned int JQ6500_Serial::getVersion() { return this->sendCommandWithUnsignedIntResponse(0x46); }
unsigned int JQ6500_Serial::countFiles(byte source)
{
if(source == MP3_SRC_SDCARD)
{
return this->sendCommandWithUnsignedIntResponse(0x47);
}
else if (source == MP3_SRC_BUILTIN)
{
return this->sendCommandWithUnsignedIntResponse(0x49);
}
return 0;
}
unsigned int JQ6500_Serial::countFolders(byte source)
{
if(source == MP3_SRC_SDCARD)
{
return this->sendCommandWithUnsignedIntResponse(0x53);
}
return 0;
}
unsigned int JQ6500_Serial::currentFileIndexNumber(byte source)
{
if(source == MP3_SRC_SDCARD)
{
return this->sendCommandWithUnsignedIntResponse(0x4B);
}
else if (source == MP3_SRC_BUILTIN)
{
return this->sendCommandWithUnsignedIntResponse(0x4D)+1; // CRAZY!
}
return 0;
}
unsigned int JQ6500_Serial::currentFilePositionInSeconds() { return this->sendCommandWithUnsignedIntResponse(0x50); }
unsigned int JQ6500_Serial::currentFileLengthInSeconds() { return this->sendCommandWithUnsignedIntResponse(0x51); }
void JQ6500_Serial::currentFileName(char *buffer, unsigned int bufferLength)
{
this->sendCommand(0x52, 0, 0, buffer, bufferLength);
}
// Used for the status commands, they mostly return an 8 to 16 bit integer
// and take no arguments
unsigned int JQ6500_Serial::sendCommandWithUnsignedIntResponse(byte command)
{
char buffer[5];
this->sendCommand(command, 0, 0, buffer, sizeof(buffer));
return (unsigned int) strtoul(buffer, NULL, 16);
}
void JQ6500_Serial::sendCommand(byte command)
{
this->sendCommand(command, 0, 0, 0, 0);
}
void JQ6500_Serial::sendCommand(byte command, byte arg1)
{
this->sendCommand(command, arg1, 0, 0, 0);
}
void JQ6500_Serial::sendCommand(byte command, byte arg1, byte arg2)
{
this->sendCommand(command, arg1, arg2, 0, 0);
}
void JQ6500_Serial::sendCommand(byte command, byte arg1, byte arg2, char *responseBuffer, unsigned int bufferLength)
{
// Command structure
// [7E][number bytes following including command and terminator][command byte][?arg1][?arg2][EF]
// Most commands do not have arguments
byte args = 0;
// These ones do
switch(command)
{
case 0x03: args = 2; break;
case 0x06: args = 1; break;
case 0x07: args = 1; break;
case 0x09: args = 1; break;
case 0x0F: args = 1; break;
case 0x11: args = 1; break;
case 0x12: args = 2; break;
}
#if MP3_DEBUG
char buf[4];
Serial.println();
Serial.print("7E ");
itoa(2+args, buf, 16); Serial.print(buf); Serial.print(" "); memset(buf, 0, sizeof(buf));
itoa(command, buf, 16); Serial.print(buf); Serial.print(" "); memset(buf, 0, sizeof(buf));
if(args>=1) itoa(arg1, buf, 16); Serial.print(buf); Serial.print(" "); memset(buf, 0, sizeof(buf));
if(args>=2) itoa(arg2, buf, 16); Serial.print(buf); Serial.print(" "); memset(buf, 0, sizeof(buf));
Serial.print("EF");
#endif
// The device appears to send some sort of status information (namely "STOP" when it stops playing)
// just discard this right before we send the command
while(this->waitUntilAvailable(10)) this->read();
this->write((byte)0x7E);
this->write(2+args);
this->write(command);
if(args>=1) this->write(arg1);
if(args==2) this->write(arg2);
this->write((byte)0xEF);
unsigned int i = 0;
char j = 0;
if(responseBuffer && bufferLength)
{
memset(responseBuffer, 0, bufferLength);
}
// Allow some time for the device to process what we did and
// respond, up to 1 second, but typically only a few ms.
this->waitUntilAvailable(1000);
#if MP3_DEBUG
Serial.print(" ==> [");
#endif
while(this->waitUntilAvailable(150))
{
j = (char)this->read();
#if MP3_DEBUG
Serial.print(j);
#endif
if(responseBuffer && (i<(bufferLength-1)))
{
responseBuffer[i++] = j;
}
}
#if MP3_DEBUG
Serial.print("]");
Serial.println();
#endif
}
// as readBytes with terminator character
// terminates if length characters have been read, timeout, or if the terminator character detected
// returns the number of characters placed in the buffer (0 means no valid data found)
size_t JQ6500_Serial::readBytesUntilAndIncluding(char terminator, char *buffer, size_t length, byte maxOneLineOnly)
{
if (length < 1) return 0;
size_t index = 0;
while (index < length) {
int c = timedRead();
if (c < 0) break;
*buffer++ = (char)c;
index++;
if(c == terminator) break;
if(maxOneLineOnly && ( c == '\n') ) break;
}
return index; // return number of characters, not including null terminator
}
// Waits until data becomes available, or a timeout occurs
int JQ6500_Serial::waitUntilAvailable(unsigned long maxWaitTime)
{
unsigned long startTime;
int c = 0;
startTime = millis();
do {
c = this->available();
if (c) break;
} while(millis() - startTime < maxWaitTime);
return c;
}