-
-
Save ZevEisenberg/bb8cf844098f488ca17447088711fce9 to your computer and use it in GitHub Desktop.
Adafruit Feather RP2040 + Music Maker FeatherWing music player sketch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <algorithm> | |
#include <Adafruit_VS1053.h> | |
#include <MFRC522.h> | |
#include <SD.h> | |
#include <SPI.h> | |
#include <Adafruit_NeoPixel.h> | |
Adafruit_NeoPixel pixels(1, PIN_NEOPIXEL); | |
const uint32_t red = pixels.Color(0xFF, 0, 0); | |
const uint32_t blue = pixels.Color(0, 0, 0xFF); | |
// Correct pins via | |
// https://flashgamer.com/blog/comments/using-feather-rp2040-with-adafruits-music-maker-featherwing | |
// These are the pins used | |
#define VS1053_RESET -1 // VS1053 reset pin (not used!) | |
// Feather RP2040 - MusicMaker FeatherWing | |
#define VS1053_CS 8 // VS1053 chip select pin (output) | |
#define VS1053_DCS 10 // VS1053 Data/command select pin (output) | |
#define CARDCS 7 // Card chip select pin | |
#define VS1053_DREQ 9 // VS1053 Data request, ideally an Interrupt pin | |
// Feather RP2040 - RC522 RFID Reader | |
#define RFID_CS_PIN 12 // chipSelectPin, aka slave select, aka SDA on RC522 | |
#define RFID_RST_PIN 11 // resetPowerDownPin | |
const int PLAY_PAUSE_BUTTON_PIN = 3; // SCL, GPIO3 | |
volatile boolean isPlaying = false; | |
volatile boolean pendingPlayPauseButtonPress = true; | |
volatile int currentTrackIndex = -1; // -1 means no track is playing | |
std::vector<String> tracks{}; | |
Adafruit_VS1053_FilePlayer musicPlayer = | |
Adafruit_VS1053_FilePlayer(VS1053_RESET, VS1053_CS, VS1053_DCS, VS1053_DREQ, CARDCS); | |
MFRC522 rfid = MFRC522(RFID_CS_PIN, RFID_RST_PIN); | |
void setup() { | |
// Some boards work best if we also make a serial connection | |
Serial.begin(115200); | |
while (!Serial) { | |
delay(1); | |
} | |
delay(500); | |
Serial.println(F("\n\nAdafruit VS1053 Feather Test")); | |
// delay(500); | |
SPI.begin(); // Init SPI bus | |
// delay(500); | |
rfid.PCD_Init(); // Start reading RFID cards | |
// We want INPUT_PULLUP so the value doesn't randomly fluctuate when not HIGH | |
pinMode(PLAY_PAUSE_BUTTON_PIN, INPUT_PULLUP); | |
pixels.begin(); | |
if (!musicPlayer.begin()) { // initialise the music player | |
Serial.println(F("Couldn't find VS1053, do you have the right pins defined?")); | |
while (1) | |
; | |
} | |
Serial.println(F("VS1053 found")); | |
// Set volume for left, right channels. lower numbers == louder volume! | |
musicPlayer.setVolume(40, 40); | |
// musicPlayer.sineTest(0x44, 500); // Make a tone to indicate VS1053 is working | |
if (!SD.begin(CARDCS)) { | |
Serial.println(F("SD failed, or not present")); | |
while (1) | |
; // don't do anything more | |
} | |
Serial.println(F("SD OK!")); | |
// DEBUGGING: list files | |
// printDirectory(SD.open("/Music"), 0); | |
loadFilesFromSDCard(SD.open("/Music")); | |
// If DREQ is on an interrupt pin we can do background | |
// audio playing. In this case, #if defined(__AVR_ATmega32U4__) will be false. | |
if (musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT)) { // DREQ int | |
Serial.println(F("Able to use interrupt")); | |
} else { | |
Serial.println(F("Can't use interrupt")); | |
} | |
attachInterrupt(digitalPinToInterrupt(PLAY_PAUSE_BUTTON_PIN), readButton, FALLING); | |
// Example Code | |
// Play a file synchronously | |
// Serial.println(F("Playing 0013 The_Car_Bunnies.mp3")); | |
// musicPlayer.playFullFile("0013 The_Car_Bunnies.mp3"); | |
// Play a file in the background, REQUIRES interrupts! | |
// Serial.println(F("0013 The_Car_Bunnies.mp3p3")); | |
// musicPlayer.startPlayingFile("0013 The_Car_Bunnies.mp3"); | |
} | |
void loop() { | |
pixels.setPixelColor(0, isPlaying ? red : blue); | |
pixels.show(); | |
if (pendingPlayPauseButtonPress) { | |
if (currentTrackIndex != -1) { | |
// Don't want to do serial things inside interrupt handler, so do it here instead. | |
musicPlayer.pausePlaying(!isPlaying); | |
} | |
pendingPlayPauseButtonPress = false; | |
} | |
if (!musicPlayer.playingMusic) { | |
// toggle isPlaying in case song finished | |
isPlaying = false; | |
} | |
readFromCard(); | |
} | |
void loadFilesFromSDCard(File dir) { | |
if (!dir.isDirectory()) { | |
Serial.println(F("Error: attempt to load files from path that is not a folder")); | |
return; | |
} | |
while (true) { | |
File entry = dir.openNextFile(); | |
if (!entry) { | |
// no more files | |
entry.close(); | |
break; | |
} | |
auto name = entry.name(); | |
Serial.print(F("Loading track ")); | |
Serial.print(tracks.size() + 1); | |
Serial.print(F(" ")); | |
Serial.println(name); | |
tracks.push_back(name); | |
} | |
sort(tracks.begin(), tracks.end()); | |
} | |
void readButton() { | |
static unsigned long lastInterruptTime = 0; | |
unsigned long interruptTime = millis(); | |
// debounce button press | |
if (interruptTime - lastInterruptTime > 500) { | |
isPlaying = !isPlaying; | |
} | |
lastInterruptTime = interruptTime; | |
pendingPlayPauseButtonPress = true; | |
} | |
void readFromCard() { | |
rfid.PCD_Init(); // Need to keep doing this in case it shuts down or something | |
Serial.println(F("A")); | |
// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. | |
if (rfid.PICC_IsNewCardPresent()) { | |
Serial.println(F("new card present")); | |
// Select one of the cards | |
if (!rfid.PICC_ReadCardSerial()) { | |
Serial.println(F("Failed to read card.")); | |
return; | |
} | |
Serial.println(F("** Card Detected: **")); | |
rfid.PICC_DumpDetailsToSerial(&(rfid.uid)); // Dump some details about the card | |
// rfid.PICC_DumpToSerial(&(rfid.uid)); // uncomment this to see all blocks in hex | |
/**** Get number and play song ****/ | |
// Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory. | |
MFRC522::MIFARE_Key key; | |
for (byte i = 0; i < 6; i++) { | |
key.keyByte[i] = 0xFF; | |
} | |
Serial.println(F("C")); | |
byte block = 1; | |
byte len = 18; | |
byte buffer2[18]; | |
MFRC522::StatusCode status = rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 1, &key, &(rfid.uid)); | |
Serial.println(F("D")); | |
if (status != MFRC522::STATUS_OK) { | |
Serial.print(F("Authentication failed: ")); | |
Serial.println(rfid.GetStatusCodeName(status)); | |
return; | |
} | |
status = rfid.MIFARE_Read(block, buffer2, &len); | |
if (status != MFRC522::STATUS_OK) { | |
Serial.print(F("Reading failed: ")); | |
Serial.println(rfid.GetStatusCodeName(status)); | |
return; | |
} | |
Serial.println(F("E")); | |
// Print Number | |
String numberString = ""; | |
for (uint8_t i = 0; i < 16; i++) { | |
numberString += (char)buffer2[i]; | |
} | |
Serial.println(F("F")); | |
numberString.trim(); | |
Serial.print(F("Number: ")); | |
Serial.println(numberString); | |
// // Play Song | |
Serial.println(F("F")); | |
currentTrackIndex = numberString.toInt(); | |
// Example so I can think about this, because array indexing is the hardest problem in computer science | |
// current track index: 14 | |
// tracks.size(): 14 | |
// highest array index: 13 | |
// currentTrackIndex must be <= tracks.size | |
// RFID cards and audio files are 1-indexed, but array is 0-indexed | |
auto currentTrackArrayIndex = currentTrackIndex - 1; | |
if (currentTrackArrayIndex < tracks.size()) { | |
Serial.println(F("G")); | |
String trackName = tracks[currentTrackArrayIndex]; | |
Serial.print(F("Playing song: ")); | |
Serial.println(trackName); | |
Serial.println(F("H")); | |
isPlaying = true; | |
musicPlayer.startPlayingFile(trackName.c_str()); | |
Serial.println(F("I")); | |
} else { | |
Serial.print(F("Attempt to play out-of-bounds track ")); | |
Serial.print(currentTrackIndex); | |
Serial.print(F(", but highest number is ")); | |
Serial.println(tracks.size() - 1); | |
} | |
// musicPlayer.startPlayingFile("0013 The_Car_Bunnies.mp3"); | |
Serial.println(F("\n** End Reading **\n")); | |
rfid.PICC_HaltA(); | |
Serial.println(F("J")); | |
rfid.PCD_StopCrypto1(); | |
Serial.println(F("K")); | |
} else { | |
Serial.println(F("No new card present")); | |
} | |
delay(1000); // change value if you want to read cards faster | |
Serial.println(F("M")); | |
} | |
/// File listing helper | |
void printDirectory(File dir, int numTabs) { | |
while (true) { | |
File entry = dir.openNextFile(); | |
if (!entry) { | |
// no more files | |
// Serial.println(F("**nomorefiles**")); | |
break; | |
} | |
for (uint8_t i = 0; i < numTabs; i++) { | |
Serial.print('\t'); | |
} | |
Serial.print(entry.name()); | |
if (entry.isDirectory()) { | |
Serial.println(F("/")); | |
printDirectory(entry, numTabs + 1); | |
} else { | |
// files have sizes, directories do not | |
Serial.print("\t\t"); | |
Serial.println(entry.size(), DEC); | |
} | |
entry.close(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment