#include <SD.h>
// Consider using DFPlayer Mini, it's a cheap MP3 component only needing two digital pins
#include <DFMP3.h>
#include <SPI.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
DFMp3 audio;
LiquidCrystal_I2C lcd(0x27, 16, 1); // Adjust the address if needed
const int chipSelect = 10;
const int buttonPins[] = {2, 3, 4, 5, 6, 7}; // Pins for the 6 buttons
const int potPin = A0; // Potentiometer pin for volume control
const int ledPin = 9; // Indicator LED pin
const int eyePins[] = {10, 11}; // Eye LEDs pins
const int encoderPinA = 12; // Rotary encoder pin A for file selection
const int encoderPinB = 13; // Rotary encoder pin B for file selection
const int encoderButtonPin = 8; // Rotary encoder push button pin
int currentButton = -1;
unsigned long currentTime = 0;
String fileNames[10]; // Array to hold filenames
int fileCount = 0;
int selectedFile = 0;
bool isOn = false;
int lastEncoderValue = 0;
void setup() {
Serial.begin(9600);
if (!SD.begin(chipSelect)) {
Serial.println("SD fail");
return;
}
//audio.speakerPin = 9;
//audio.setVolume(5);
audio.begin();
lcd.begin();
lcd.backlight();
for (int i = 0; i < 6; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
pinMode(potPin, INPUT);
pinMode(ledPin, OUTPUT);
for (int i = 0; i < 2; i++) {
pinMode(eyePins[i], OUTPUT);
}
pinMode(encoderPinA, INPUT);
pinMode(encoderPinB, INPUT);
pinMode(encoderButtonPin, INPUT_PULLUP);
loadFileNames();
}
void loop() {
int potValue = analogRead(potPin);
if (digitalRead(powerPin) == LOW) {
if (!isOn) {
isOn = true;
digitalWrite(ledPin, HIGH);
audio.play(fileNames[selectedFile].c_str());
}
audio.setVolume(map(potValue, 0, 1023, 0, 7));
} else {
if (isOn) {
isOn = false;
digitalWrite(ledPin, LOW);
audio.stopPlayback();
}
}
if (isOn) {
for (int i = 0; i < 4; i++) {
if (digitalRead(buttonPins[i]) == LOW) {
if (currentButton != i) {
currentTime = audio.getCurrentPosition();
audio.stopPlayback();
delay(100); // Debounce delay
audio.play(fileNames[i].c_str(), currentTime);
currentButton = i;
}
}
}
if (digitalRead(buttonPins[4]) == LOW) {
savePosition();
}
if (digitalRead(buttonPins[5]) == LOW) {
restorePosition();
}
int encoderValue = readEncoder();
if (encoderValue != lastEncoderValue) {
selectedFile = (selectedFile + (encoderValue - lastEncoderValue)) % fileCount;
if (selectedFile < 0) selectedFile += fileCount;
lastEncoderValue = encoderValue;
}
if (digitalRead(encoderButtonPin) == LOW) {
audio.stopPlayback();
delay(100); // Debounce delay
audio.play(fileNames[selectedFile].c_str());
}
lcd.setCursor(0, 0);
lcd.print("Playing: ");
lcd.print(fileNames[selectedFile].substring(0, fileNames[selectedFile].indexOf('-')));
// Adjust LED brightness based on audio data
int audioLevel = audio.getLevel(); // Get the audio level from TMRpcm
int brightness = map(audioLevel, 0, 255, 64, 255); // Map to 25% (64) to 100% (255)
analogWrite(eyePins[0], brightness);
analogWrite(eyePins[1], brightness);
}
}
int readEncoder() {
static int lastA = LOW;
static int lastB = LOW;
int newA = digitalRead(encoderPinA);
int newB = digitalRead(encoderPinB);
int result = 0;
if (newA != lastA || newB != lastB) {
if (lastA == LOW && newA == HIGH) {
if (newB == LOW) result = -1;
else result = 1;
}
lastA = newA;
lastB = newB;
}
return result;
}
void savePosition() {
String posFileName = fileNames[selectedFile].substring(0, fileNames[selectedFile].indexOf('-')) + ".pos";
File file = SD.open(posFileName.c_str(), FILE_WRITE);
if (file) {
file.println(selectedFile);
file.println(audio.getCurrentPosition());
file.close();
}
}
void restorePosition() {
String posFileName = fileNames[selectedFile].substring(0, fileNames[selectedFile].indexOf('-')) + ".pos";
File file = SD.open(posFileName.c_str());
if (file) {
selectedFile = file.parseInt();
currentTime = file.parseInt();
file.close();
audio.play(fileNames[selectedFile].c_str(), currentTime);
}
}
void loadFileNames() {
File root = SD.open("/");
fileCount = 0;
while (true) {
File entry = root.openNextFile();
if (!entry) {
break;
}
if (!entry.isDirectory() && String(entry.name()).endsWith(".wav")) {
fileNames[fileCount++] = String(entry.name());
}
entry.close();
}
}