Files
awtrix-light/lib/DFMiniMp3-1.0.7-noChecksums/src/DFMiniMp3.h
Elfish 055d54d099 Split code between ulanzi environment and awtrix_upgrade
Added BME280 sensor support for awtrix_upgrade.

Removed battery readings for awtrix_upgrade

Added DFMiniMp3 lib for replacing the Ulanzi buzzer in the future
2023-03-31 15:12:13 +02:00

560 lines
13 KiB
C++

/*-------------------------------------------------------------------------
DFMiniMp3 library
Written by Michael C. Miller.
I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/DFMiniMp3)
-------------------------------------------------------------------------
This file is part of the Makuna/DFMiniMp3 library.
DFMiniMp3 is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
DFMiniMp3 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with DFMiniMp3. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/
#pragma once
enum DfMp3_Error
{
// from device
DfMp3_Error_Busy = 1,
DfMp3_Error_Sleeping,
DfMp3_Error_SerialWrongStack,
DfMp3_Error_CheckSumNotMatch,
DfMp3_Error_FileIndexOut,
DfMp3_Error_FileMismatch,
DfMp3_Error_Advertise,
// from library
DfMp3_Error_RxTimeout = 0x81,
DfMp3_Error_PacketSize,
DfMp3_Error_PacketHeader,
DfMp3_Error_PacketChecksum,
DfMp3_Error_General = 0xff
};
enum DfMp3_PlaybackMode
{
DfMp3_PlaybackMode_Repeat,
DfMp3_PlaybackMode_FolderRepeat,
DfMp3_PlaybackMode_SingleRepeat,
DfMp3_PlaybackMode_Random
};
enum DfMp3_Eq
{
DfMp3_Eq_Normal,
DfMp3_Eq_Pop,
DfMp3_Eq_Rock,
DfMp3_Eq_Jazz,
DfMp3_Eq_Classic,
DfMp3_Eq_Bass
};
enum DfMp3_PlaySource // value - only one can be set
{
DfMp3_PlaySource_Usb = 1,
DfMp3_PlaySource_Sd,
DfMp3_PlaySource_Aux,
DfMp3_PlaySource_Sleep,
DfMp3_PlaySource_Flash
};
enum DfMp3_PlaySources // bitfield - more than one can be set
{
DfMp3_PlaySources_Usb = 0x01,
DfMp3_PlaySources_Sd = 0x02,
DfMp3_PlaySources_Pc = 0x04,
DfMp3_PlaySources_Flash = 0x08,
};
template <class T_SERIAL_METHOD, class T_NOTIFICATION_METHOD>
class DFMiniMp3
{
public:
DFMiniMp3(T_SERIAL_METHOD &serial) : _serial(serial),
_lastSendSpace(c_msSendSpace),
_isOnline(false)
{
}
void begin(unsigned long baud = 9600)
{
_serial.begin(baud);
_serial.setTimeout(10000);
_lastSend = millis();
}
void loop()
{
while (_serial.available() >= DfMp3_Packet_SIZE)
{
listenForReply(0x00);
}
}
// Does not work with all models.
// YX5200-24SS - sends reply
// MH2024K-24SS - sends NO reply --> results in error notification
DfMp3_PlaySources getPlaySources()
{
drainResponses();
sendPacket(0x3f);
return static_cast<DfMp3_PlaySources>(listenForReply(0x3f));
}
// the track as enumerated across all folders
void playGlobalTrack(uint16_t track = 0)
{
sendPacket(0x03, track);
}
// sd:/mp3/####track name
void playMp3FolderTrack(uint16_t track)
{
sendPacket(0x12, track);
}
// older devices: sd:/###/###track name
// newer devices: sd:/##/###track name
// folder and track numbers are zero padded
void playFolderTrack(uint8_t folder, uint8_t track)
{
uint16_t arg = (folder << 8) | track;
sendPacket(0x0f, arg);
}
// sd:/##/####track name
// track number must be four digits, zero padded
void playFolderTrack16(uint8_t folder, uint16_t track)
{
uint16_t arg = (((uint16_t)folder) << 12) | track;
sendPacket(0x14, arg);
}
void playRandomTrackFromAll()
{
sendPacket(0x18);
}
void nextTrack()
{
sendPacket(0x01);
}
void prevTrack()
{
sendPacket(0x02);
}
uint16_t getCurrentTrack(DfMp3_PlaySource source = DfMp3_PlaySource_Sd)
{
drainResponses();
uint8_t command;
switch (source)
{
case DfMp3_PlaySource_Usb:
command = 0x4b;
break;
case DfMp3_PlaySource_Sd:
command = 0x4c;
break;
case DfMp3_PlaySource_Flash:
command = 0x4d;
break;
default:
command = 0x4c;
break;
}
sendPacket(command);
return listenForReply(command);
}
// 0- 30
void setVolume(uint8_t volume)
{
sendPacket(0x06, volume);
}
uint8_t getVolume()
{
drainResponses();
sendPacket(0x43);
return static_cast<uint8_t>(listenForReply(0x43));
}
void increaseVolume()
{
sendPacket(0x04);
}
void decreaseVolume()
{
sendPacket(0x05);
}
// useless, removed
// 0-31
/*
void setVolume(bool mute, uint8_t volume)
{
uint16_t arg = (!mute << 8) | volume;
sendPacket(0x10, arg);
}
*/
void loopGlobalTrack(uint16_t globalTrack)
{
sendPacket(0x08, globalTrack);
}
// sd:/##/*
// 0-99
void loopFolder(uint8_t folder)
{
sendPacket(0x17, folder);
}
DfMp3_PlaybackMode getPlaybackMode()
{
drainResponses();
sendPacket(0x45);
return static_cast<DfMp3_PlaybackMode>(listenForReply(0x45));
}
void setRepeatPlayAllInRoot(bool repeat)
{
sendPacket(0x11, !!repeat);
}
void setRepeatPlayCurrentTrack(bool repeat)
{
sendPacket(0x19, !repeat);
}
void setEq(DfMp3_Eq eq)
{
sendPacket(0x07, eq);
}
DfMp3_Eq getEq()
{
drainResponses();
sendPacket(0x44);
return static_cast<DfMp3_Eq>(listenForReply(0x44));
}
void setPlaybackSource(DfMp3_PlaySource source)
{
sendPacket(0x09, source, 200);
}
void sleep()
{
sendPacket(0x0a);
}
void reset()
{
sendPacket(0x0c, 0, 600);
_isOnline = false;
}
void start()
{
sendPacket(0x0d);
}
void pause()
{
sendPacket(0x0e);
}
void stop()
{
sendPacket(0x16);
}
uint16_t getStatus()
{
drainResponses();
sendPacket(0x42);
return listenForReply(0x42);
}
uint16_t getFolderTrackCount(uint16_t folder)
{
drainResponses();
sendPacket(0x4e, folder);
return listenForReply(0x4e);
}
uint16_t getTotalTrackCount(DfMp3_PlaySource source)
{
drainResponses();
uint8_t command;
switch (source)
{
case DfMp3_PlaySource_Usb:
command = 0x47;
break;
case DfMp3_PlaySource_Sd:
command = 0x48;
break;
case DfMp3_PlaySource_Flash:
command = 0x49;
break;
default:
command = 0x48;
break;
}
sendPacket(command);
return listenForReply(command);
}
uint16_t getTotalFolderCount()
{
drainResponses();
sendPacket(0x4F);
return listenForReply(0x4F);
}
// sd:/advert/####track name
void playAdvertisement(uint16_t track)
{
sendPacket(0x13, track);
}
void stopAdvertisement()
{
sendPacket(0x15);
}
void enableDac()
{
sendPacket(0x1A, 0x00);
}
void disableDac()
{
sendPacket(0x1A, 0x01);
}
bool isOnline() const
{
return _isOnline;
}
private:
static const uint16_t c_msSendSpace = 50;
// 7E FF 06 0F 00 01 01 xx xx EF
// 0 -> 7E is start code
// 1 -> FF is version
// 2 -> 06 is length
// 3 -> 0F is command
// 4 -> 00 is no receive
// 5~6 -> 01 01 is argument
// 7~8 -> checksum = 0 - ( FF+06+0F+00+01+01 )
// 9 -> EF is end code
enum DfMp3_Packet
{
DfMp3_Packet_StartCode,
DfMp3_Packet_Version,
DfMp3_Packet_Length,
DfMp3_Packet_Command,
DfMp3_Packet_RequestAck,
DfMp3_Packet_HiByteArgument,
DfMp3_Packet_LowByteArgument,
DfMp3_Packet_HiByteCheckSum,
DfMp3_Packet_LowByteCheckSum,
DfMp3_Packet_EndCode,
DfMp3_Packet_SIZE
};
enum DfMp3_Packet_short
{
DfMp3_Packet_short_StartCode,
DfMp3_Packet_short_Version,
DfMp3_Packet_short_Length,
DfMp3_Packet_short_Command,
DfMp3_Packet_short_RequestAck,
DfMp3_Packet_short_HiByteArgument,
DfMp3_Packet_short_LowByteArgument,
// DfMp3_Packet_short_HiByteCheckSum,
// DfMp3_Packet_short_LowByteCheckSum,
DfMp3_Packet_short_EndCode,
DfMp3_Packet_short_SIZE
};
T_SERIAL_METHOD &_serial;
uint32_t _lastSend;
uint16_t _lastSendSpace;
bool _isOnline;
void drainResponses()
{
while (_serial.available() > 0)
{
listenForReply(0x00);
}
}
void sendPacket(uint8_t command, uint16_t arg = 0, uint16_t sendSpaceNeeded = c_msSendSpace)
{
uint8_t out[DfMp3_Packet_short_SIZE] = {0x7E,
0xFF,
06,
command,
00,
static_cast<uint8_t>(arg >> 8),
static_cast<uint8_t>(arg & 0x00ff),
// 00,
// 00,
0xEF};
// setChecksum(out);
// wait for spacing since last send
while (((millis() - _lastSend) < _lastSendSpace))
{
// check for event messages from the device while
// we wait
loop();
delay(1);
}
_lastSendSpace = sendSpaceNeeded;
_serial.write(out, DfMp3_Packet_short_SIZE);
_lastSend = millis();
}
bool readPacket(uint8_t *command, uint16_t *argument)
{
uint8_t in[DfMp3_Packet_SIZE] = {0};
uint8_t read;
// init our out args always
*command = 0;
*argument = 0;
// try to sync our reads to the packet start
do
{
// we use readBytes as it gives us the standard timeout
read = _serial.readBytes(&(in[DfMp3_Packet_StartCode]), 1);
if (read != 1)
{
// nothing read
*argument = DfMp3_Error_RxTimeout;
return false;
}
} while (in[DfMp3_Packet_StartCode] != 0x7e);
read += _serial.readBytes(in + 1, DfMp3_Packet_SIZE - 1);
if (read < DfMp3_Packet_SIZE)
{
// not enough bytes, corrupted packet
*argument = DfMp3_Error_PacketSize;
return false;
}
if (in[DfMp3_Packet_Version] != 0xFF ||
in[DfMp3_Packet_Length] != 0x06 ||
in[DfMp3_Packet_EndCode] != 0xef)
{
// invalid version or corrupted packet
*argument = DfMp3_Error_PacketHeader;
return false;
}
// if (!validateChecksum(in))
// {
// // checksum failed, corrupted packet
// *argument = DfMp3_Error_PacketChecksum;
// return false;
// }
*command = in[DfMp3_Packet_Command];
*argument = ((in[DfMp3_Packet_HiByteArgument] << 8) | in[DfMp3_Packet_LowByteArgument]);
return true;
}
uint16_t listenForReply(uint8_t command)
{
uint8_t replyCommand = 0;
uint16_t replyArg = 0;
do
{
if (readPacket(&replyCommand, &replyArg))
{
if (command != 0 && command == replyCommand)
{
return replyArg;
}
else
{
}
}
else
{
if (replyArg != 0)
{
if (_serial.available() == 0)
{
return 0;
}
}
}
} while (command != 0);
return 0;
}
// uint16_t calcChecksum(uint8_t *packet)
// {
// uint16_t sum = 0xFFFF;
// for (int i = DfMp3_Packet_Version; i < DfMp3_Packet_HiByteCheckSum; i++)
// {
// sum -= packet[i];
// }
// return sum + 1;
// }
// void setChecksum(uint8_t *out)
// {
// uint16_t sum = calcChecksum(out);
// out[DfMp3_Packet_HiByteCheckSum] = ((sum >> 8) & 0xFF);
// out[DfMp3_Packet_LowByteCheckSum] = (sum & 0xff);
// }
// bool validateChecksum(uint8_t *in)
// {
// uint16_t sum = calcChecksum(in);
// return (sum == static_cast<uint16_t>((in[DfMp3_Packet_HiByteCheckSum] << 8) | in[DfMp3_Packet_LowByteCheckSum]));
// }
};