Skip to content

Instantly share code, notes, and snippets.

@ZevEisenberg
Created March 31, 2023 03:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ZevEisenberg/bb8cf844098f488ca17447088711fce9 to your computer and use it in GitHub Desktop.
Save ZevEisenberg/bb8cf844098f488ca17447088711fce9 to your computer and use it in GitHub Desktop.
Adafruit Feather RP2040 + Music Maker FeatherWing music player sketch
#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