Code below as requested:
/*
Wed 22 Feb 2023 2037:
Random mode OK. Normal mode OK. Volume,Toggle etc now working
Still working on gettin Pause/Play working reliably.
A few constants and variables no longer needed; awaits cleanup.
"Sketch uses 10722 bytes (33%) of program storage space. Maximum is 32256 bytes.
Global variables use 1638 bytes (79%) of dynamic memory, leaving 410 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur."
*/
//#include "Arduino.h" // Redundant
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
#include "Toggle.h" // (Author David Lloyd) To use multiple button presses
// PINS
// 0 Unused
// 1 Unused
// 2 Spare buttons for experiment.
const byte pausePin = 3; // Toggles Pause/Play (WAS 3)
const byte yellowLedPin = 4;
//const byte ledPin = 5; // For code that seems to ensure starting on powerup
const byte previousPin = 6; // Previous
const byte nextPin = 7; // Next
const byte volupPin = 8; //Vol up
const byte voldownPin = 9; // Vol down
// 10 = Rx
// 11 = Tx
const byte BUSY_PIN = 12;
// 13 Unused & built-in LED
// A0 Unused
// A1 Unused
// CONSTANTS
const int btnGrpDelay = 500; // Have used 500, 1000, 2000, 50, 10, 5, 0
const byte folderPin = A5; // For Toggle library
const byte jumpTenBackPin = A3; // Jump 10 back
const byte jumpTenFwdPin = A2; // Jump 10 forward
const byte modePin = A4; // SWITCH, not button, HIGH = Normal, LOW (sw to right) = Random
// VARIABLES
bool busyPinState = HIGH;
int currentTrack; // Used in bit array method
byte fldr;
byte folderCode; // For Toggle library
int folderSize;
int i;
bool mode;
byte numFldrs = 26;
bool pauseBtnState = 1; // H = Not pressed
bool paused = false;
bool pauseState = 0; // Not paused
bool prevBusyPinState = LOW;
bool prevMode;
int randNumberF;
int randomTrack; // Used in bit array method
int randTrk;
int track;
int trackCount = 1651;
unsigned long lastPlayTime = 0;
unsigned long busyCheckDelay = 300; // 600 300 1001000 200
// ARRAYS
// For 26 folder special, short content, e.g msd 'S6'
int trackArray[254]; // Max possible tracks in any folder
const int cumulTracks[26] {20, 24, 26, 30, 31, 35, 38, 42, 79, 105,
183, 435, 506, 575, 760, 784, 865, 1027, 1212, 1289, 1380, 1415,
1452, 1495, 1568, 1651
};
const byte folderSizes[26] {20, 4, 2, 4, 1, 4, 3, 4, 37, 26, 78,
252, 71, 69, 185, 24, 81, 162, 185, 77, 91, 35, 37, 43, 73,
83
};
// 'Special Short' content for bit array testing with 1651 short tracks; (allow 210 bytes for bit array)
// Full project has 2816 tracks, so array will need say 360 bytes
// Which look like a show stopper because of memory shortage
byte playedTracks[210];
SoftwareSerial mySoftwareSerial(10, 11);
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);
Toggle folder(folderPin); // For Toggle library
// _____ ______ _______ _ _ _____
// / ____| ____|__ __| | | | __ \
// | (___ | |__ | | | | | | |__) |
// \___ \| __| | | | | | | ___/
// ____) | |____ | | | |__| | |
// |_____/|______| |_| \____/|_|
void setup()
{
// pinMode(bluLedPin, OUTPUT); // D5 (temp blue LED), now redundant?
pinMode(folderPin, INPUT_PULLUP); // A5 for Toggle
pinMode(jumpTenBackPin, INPUT_PULLUP); // A3
pinMode(jumpTenFwdPin, INPUT_PULLUP); // A2
pinMode(modePin, INPUT_PULLUP); // A4
pinMode(nextPin, INPUT_PULLUP); // D7
pinMode(pausePin, INPUT_PULLUP); // D3
pinMode(previousPin, INPUT_PULLUP); // D6
pinMode(voldownPin, INPUT_PULLUP); // D9
pinMode(volupPin, INPUT_PULLUP); // D8
// pinMode(yellowLedPin, OUTPUT); // D4 (temp yellow LED)
mySoftwareSerial.begin(9600);
const char * compileFilename = __FILE__;
Serial.begin(115200);
delay(200);
Serial.println(F("=============================================="));
Serial.println( F("Code running comes from file ") );
Serial.print("Full path : "); Serial.println(compileFilename);
const char * lastSeparator = strrchr(compileFilename, '\\');
if (lastSeparator != nullptr)
{
Serial.print(F("File name : "));
Serial.println(lastSeparator);
}
Serial.print( F("Compiled: ") );
Serial.print( F(__DATE__) );
Serial.print( F(" ") );
Serial.println( F(__TIME__) );
folder.begin(folderPin); // Button being used for Toggle library
Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
delay(1000);
if (!myDFPlayer.begin(mySoftwareSerial, true, false)) // My edit
{
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
while (true);
}
Serial.println(F("DFPlayer Mini online."));
myDFPlayer.setTimeOut(1000);
//----Set volume----
myDFPlayer.volume(15);
delay(500);
randomSeed(analogRead(6) + analogRead(7));
chooseRandFolder(); // Function
createTrackArray(); // Function.
// For possible use directly after power up
myDFPlayer.stop(); // Needed?
delay(100);
i = 0;
// MODE SELECTION from 1P2W switch on A4
mode = digitalRead(modePin); // 1 = Normal, 0 = Random
prevMode = mode; // May not be used?
Serial.print(F("Setup: switch mode = "));
Serial.print(mode);
Serial.print(F("\tprevMode = "));
Serial.println(prevMode);
Serial.println(F("End setup ---------------------------------"));
} // End setup
// _ ____ ____ _____
// | | / __ \ / __ \| __ \
// | | | | | | | | | |__) |
// | | | | | | | | | ___/
// | |___| |__| | |__| | |
// |______\____/ \____/|_|
void loop()
{
// Check BUSY pin
if (millis() - lastPlayTime >= 300) // 300 1000
{
lastPlayTime = millis();
busyPinState = digitalRead(BUSY_PIN);
// If the BUSY pin goes high (i.e., track is finished), play the next track
if (busyPinState == HIGH && prevBusyPinState == LOW)
{
if (mode == 0) // Random using bit array method
{
randomTrack = random(0, trackCount - 1); // Raw
int byteIndex = randomTrack / 8;
int bitIndex = randomTrack % 8;
if (bitRead(playedTracks[byteIndex], bitIndex))
{
Serial.print(randomTrack);
Serial.println(" would be a repetition");
return;
}
myDFPlayer.play(randomTrack);
bitWrite(playedTracks[byteIndex], bitIndex, 1); // mark track as played
// Serial.print("raw randomTrack = ");
randToFT();
}
else if (mode == 1)
{
playTrackArray();
}
}
prevBusyPinState = busyPinState;
} // End millis & busy check
// prevBusyPinState = busyPinState;
//---------------------------------------------------
folder.poll(); // folder assigned to folderPin, for Toggle library
folderCode = folder.pressCode(1); // print: () on () off . For Toggle library
//---------------------------------------------------
actionCases(); // Based on Toggle library (number of actions = folder size)
//---------------------------------------------------
// VOLUME
if (digitalRead(volupPin) == LOW)
{
myDFPlayer.volumeUp();
delay(50); //
}
if (digitalRead(voldownPin) == LOW)
{
myDFPlayer.volumeDown();
delay(50);
}
// //---------------------------------------------------
// NEXT
if (digitalRead(nextPin) == LOW) // D7
{
// Thu 2 Feb 2023 1125 try this
if (mode == 0)
{
mode = 1;
myDFPlayer.stop();
delay(30);
}
myDFPlayer.next();
delay(400); // 400
}
//---------------------------------------------------
// PREVIOUS
if (digitalRead(previousPin) == LOW) // D6
{
myDFPlayer.previous();
delay(400); // 200 failed, 250 & 300 OK
}
//---------------------------------------------------
// PAUSE/PLAY
pauseBtnState = digitalRead(pausePin);
// Serial.print(F("pauseState = "));
// Serial.println(pauseBtnState);
if (pauseBtnState == LOW)
{
myDFPlayer.pause();
// myDFPlayer.stop();
pauseState = !pauseState;
if (pauseState == 0)
{
Serial.println(F("Continue..."));
myDFPlayer.start();
delay(400); // 1000
}
if (pauseState == 1)
{
Serial.println(F("Music Paused!"));
myDFPlayer.pause();
delay(400); // 1000
}
}
//---------------------------------------------------
// JUMP 10 FORWARD
if (digitalRead(jumpTenFwdPin) == LOW)
{
Serial.print(F("trackArray[i] = "));
Serial.println(trackArray[i]);
jumpTenFwd();
}
//---------------------------------------------------
// JUMP 10 BACK
if (digitalRead(jumpTenBackPin) == LOW)
{
Serial.print(F("trackArray[i] = "));
Serial.println(trackArray[i]);
jumpTenBack();
}
//---------------------------------------------------
} // End loop
// ______ _ _ _ _ _____ _______ _____ ____ _ _ _____
// | ____| | | | \ | |/ ____|__ __|_ _/ __ \| \ | |/ ____|
// | |__ | | | | \| | | | | | || | | | \| | (___
// | __| | | | | . ` | | | | | || | | | . ` |\___ \
// | | | |__| | |\ | |____ | | _| || |__| | |\ |____) |
// |_| \____/|_| \_|\_____| |_| |_____\____/|_| \_|_____/
void randomWork() // Random mode processing (bit array)
{
randomTrack = random(0, trackCount - 1); // Raw
// Serial.print(F("randomTrack = "));
// Serial.println(randomTrack);
int byteIndex = randomTrack / 8;
int bitIndex = randomTrack % 8;
if (bitRead(playedTracks[byteIndex], bitIndex))
{
Serial.print(randomTrack);
Serial.println(F(" would be a repetition"));
return;
}
myDFPlayer.play(randomTrack);
bitWrite(playedTracks[byteIndex], bitIndex, 1); // mark track as played
// Serial.print("raw randomTrack = ");
randToFT();
}
// --------------------
void normalWork()
{
playTrackArray();
}
// --------------------
// Moved random folder choice from setup(); RUN in createTrackArray()
void chooseRandFolder()
{
randNumberF = random(1, 27); // Choose random folder 1-26
// Temporarily override, to start with specific folder
// randNumberF = 19;
folderSize = folderSizes[randNumberF - 1];
Serial.print(F("randNumberF = "));
Serial.print(randNumberF);
Serial.print(F("\t"));
Serial.print(F("folderSize = "));
Serial.println(folderSize);
}
// --------------------
void createTrackArray() // Used in setup() & Cases
{
// chooseRandFolder();
// When folder size is known, an array of its original track
// numbers can be created, e.g from 1 to 26
for (int i = 0; i < folderSize; i++)
{
trackArray[i] = i + 1;
}
// Randomise (shuffle) the folder tracks
for (int i = 0; i < folderSize; i++)
{
int pos = random(folderSize);
int t = trackArray[i];
trackArray[i] = trackArray[pos];
trackArray[pos] = t;
}
i = 0;
} // End createTrackArray()
// --------------------
void playTrackArray()
{
// Only for Normal mode
if (mode == 1)
{
// module must have just stopped being busy.
if ( ( busyPinState != prevBusyPinState ) && ( busyPinState == HIGH ) )
{
// prevBusyPinState = busyPinState; // Reset
// When i > folder range
if (i != folderSize) // One of folder's tracks
{
Serial.print(F("Playing ("));
Serial.print(randNumberF);
Serial.print(F(", "));
Serial.print(trackArray[i]);
Serial.println(")");
myDFPlayer.playFolder( randNumberF , trackArray[i] );
// lastPlayTime = millis(); // Used elsewhere in loop() to
// determine busy state at frequent intervals
i++; // Increment index
} // End full range processing
// Unsure if 'else' was needed below as it works OK without
if (i == folderSize) // So just gone over range
{
// Choose a new folder.
randNumberF = random(1, 27); // Folders 1 to 26
folderSize = folderSizes[randNumberF - 1];
createTrackArray();
delay(1000); // 1000 but lower values may also work
} // End special condition
}
}
prevBusyPinState = busyPinState; // Reset
}
// --------------------
void getRaw() // Gets raw from DFR (currentTrack) then calculates
// fldr and track.
{
currentTrack = myDFPlayer.readCurrentFileNumber();
delay(200); // 2000
// Folder 1 code is special
if (currentTrack <= cumulTracks[0]) // It's in folder 1
{
fldr = 1;
track = currentTrack;
}
// Now test other ranges, e.g. for 26 folders test against
// cumulTracks[] elements [1] to [25] (array indexes start at 0).
for (int i = 1; i < numFldrs - 1; i++)
{
if (currentTrack <= cumulTracks[i] && currentTrack > cumulTracks[i - 1])
{
fldr = i + 1;
track = currentTrack - cumulTracks[i - 1];
}
}
} // End getRaw
// --------------------
// Convert currentTrack to (fldr, track) Redundant?
void rawToFT()
{
// Folder 1 code is special
if (currentTrack <= cumulTracks[0]) // It's in folder 1
{
fldr = 1;
track = currentTrack;
}
// Now test other ranges, e.g. for 26 folders test against
// cumulTracks[] elements [1] to [25] (array indexes start at 0).
for (int i = 1; i < numFldrs - 1; i++)
{
if (currentTrack <= cumulTracks[i] && currentTrack > cumulTracks[i - 1])
{
fldr = i + 1;
track = currentTrack - cumulTracks[i - 1];
}
}
}
// --------------------
// Convert randomTrack to (fldr, track) & print
void randToFT()
{
// For 'special, short' content, e.g msd 'S6'
// e.g cumulTracks[0] = 20, cumulTracks[26] = 1651; 18 Feb 2023
for (int i = 0; i < numFldrs - 1; i++)
{
if (randomTrack <= trackCount && randomTrack > cumulTracks[i - 1])
{
fldr = i + 1;
track = randomTrack - cumulTracks[i - 1];
}
}
// Serial.print(F("mode = "));
// Serial.println(mode);
Serial.print(F("raw randomTrack = "));
Serial.print(randomTrack);
Serial.print(F(" fldr = "));
Serial.print(fldr);
Serial.print(F(" track = "));
Serial.println(track);
Serial.println();
}
// --------------------
void printRandomTrack()
{
Serial.print(F("Track started = "));
Serial.print(F("("));
Serial.print(fldr);
Serial.print(F(", "));
Serial.print(track);
Serial.println(F(")"));
// Serial.println();
} // printRandomTrack()
// --------------------
void jumpTenFwd() // Basic method
{
myDFPlayer.volume(0); //Set volume value (0~30).
delay(10);
for (int i = 1; i < 11; i++)
{
myDFPlayer.next();
delay(250); // 200 50 10 50 100 500, 200, seems sensitive
}
myDFPlayer.volume(15);
delay(50);
}
// --------------------
void jumpTenBack() // Basic method
{
myDFPlayer.volume(0); //Set volume value (0~30).
delay(10);
for (int i = 1; i < 11; i++)
{
myDFPlayer.previous();
delay(250); // last 200 150 50 10 50 100 400 Was 500, seems sensitive
}
myDFPlayer.volume(15);
delay(50);
}
// --------------------
//////////////////////// CASES /////////////////////////////////
void actionCases() // Using Toggle library button
// Wed 8 Feb 2023 1154: Add more cases 27-30.
{
switch (folderCode) // Obviousl depends on mSD content
{
case 0xF1: // Want to play all 20 tracks of folder 1 from trackArray[]
mode = 1; // Not sure these are needed, but leave for now
Serial.println();
Serial.println("Case 1 has been selected");
randNumberF = 1; // 01-1950 UK No 1s
folderSize = 20;
i = 0; // Crucial to have this before playTrackArray();
createTrackArray(); // Same as used successfully in setup()
myDFPlayer.stop(); // Not sure these are needed?
// Wednesday 28 December 2022, 1511 test if following is redundant
// playTrackArray(); // That works - but strangely so does this
// myDFPlayer.playFolder(randNumberF, trackArray[0]);
delay(btnGrpDelay); // See spreadsheet TEMP
playTrackArray();
break;
case 0xF2:
mode = 1;
Serial.println();
Serial.println("Case 2 has been selected");
randNumberF = 2; // 02-1950-54 Pop
folderSize = 4; //
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0xF3:
mode = 1;
Serial.println("Case 3 has been selected");
randNumberF = 3; // 03-1955-57 Pop
folderSize = 2;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0xF4:
mode = 1;
Serial.println("Case 4 has been selected");
randNumberF = 4; // 04-1958-59 Pop
folderSize = 4;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0xF5:
mode = 1;
Serial.println("Case 5 has been selected");
randNumberF = 5; // 05-1960s Pop A-G
folderSize = 1;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0xF6:
mode = 1;
Serial.println("Case 6 has been selected");
randNumberF = 6; // 06-1960s Pop H-Z
folderSize = 4;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0xF7:
mode = 1;
Serial.println("Case 7 has been selected");
randNumberF = 7; // 07-1970s Pop
folderSize = 3;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0xF8:
mode = 1;
Serial.println("Case 8 has been selected");
randNumberF = 8; // 08-1980s Pop
folderSize = 4;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0xF9:
mode = 1;
randNumberF = 9; // Acoustic Alchemy
folderSize = 37;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x10:
mode = 1;
Serial.println();
randNumberF = 10; // Streisand
folderSize = 26;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x11:
mode = 1;
randNumberF = 11; // Beatles
folderSize = 78;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x12:
mode = 1;
randNumberF = 12; // Big Band
folderSize = 252;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x13:
mode = 1;
randNumberF = 13; // Christmas
folderSize = 71;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x14:
mode = 1;
randNumberF = 14; // Classical
folderSize = 69;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x15:
mode = 1;
randNumberF = 15; // Conniff
folderSize = 185;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x16:
mode = 1;
randNumberF = 16; // Ella
folderSize = 24;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x17:
mode = 1;
randNumberF = 17; // Elvis
folderSize = 81;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x18:
mode = 1;
randNumberF = 18; // FAVOURITES
folderSize = 162;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x19:
mode = 1;
randNumberF = 19; // Sinatra
folderSize = 185;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x20:
mode = 1;
randNumberF = 20; // Jazz
folderSize = 77;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x21:
mode = 1;
randNumberF = 21; // Movies & Musicals
folderSize = 91;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x22:
mode = 1;
randNumberF = 22; // Ray Charles
folderSize = 33;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x23:
mode = 1;
randNumberF = 23; // Simply Red
folderSize = 37;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x24:
mode = 1;
randNumberF = 24; // Swingle
folderSize = 43;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x25:
mode = 1;
randNumberF = 25; // Trad Jazz
folderSize = 73;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x26:
mode = 1;
randNumberF = 26; // TV Themes
folderSize = 83;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
//---------------------------
case 0x27:
mode = 1;
randNumberF = 27;
folderSize = 3;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x28:
mode = 1;
randNumberF = 28;
folderSize = 3;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x29:
mode = 1;
randNumberF = 29;
folderSize = 3;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
case 0x30:
mode = 1;
randNumberF = 29;
folderSize = 3;
i = 0;
createTrackArray();
myDFPlayer.stop();
delay(btnGrpDelay);
playTrackArray();
break;
default:
// statements
break;
}
}
/////////////////////// DFR FUNCTION ////////////////////////////
void printDetail(uint8_t type, int value)
{
switch (type)
{
case TimeOut:
Serial.println(F("Time Out!"));
break;
case WrongStack:
Serial.println(F("Stack Wrong!"));
break;
case DFPlayerCardInserted:
Serial.println(F("Card Inserted!"));
break;
case DFPlayerCardRemoved:
Serial.println(F("Card Removed!"));
break;
case DFPlayerCardOnline:
Serial.println(F("Card Online!"));
break;
case DFPlayerUSBInserted:
Serial.println(F("USB Inserted!"));
break;
case DFPlayerUSBRemoved:
Serial.println(F("USB Removed!"));
break;
case DFPlayerPlayFinished:
// Serial.println(F(" Play Finished!"));
break;
case DFPlayerError:
Serial.print(F("DFPlayerError : "));
switch (value)
{
case Busy:
Serial.println(F("Card not found"));
break;
case Sleeping:
Serial.println(F("Sleeping"));
break;
case SerialWrongStack:
Serial.println(F("Get Wrong Stack"));
break;
case CheckSumNotMatch:
Serial.println(F("Check Sum Not Match"));
break;
case FileIndexOut:
Serial.println(F("File Index Out of Bound"));
break;
case FileMismatch:
Serial.println(F("Cannot Find File"));
break;
case Advertise:
Serial.println(F("In Advertise"));
break;
default:
break;
}
break;
default:
break;
}
}