Feather M0 Express Arduino IDE Setup

We'll upload the code to the Adafruit Feather M0 Express using the Arduino IDE. We'll need to have the Adafruit board profile and libraries installed before uploading the code. If you're new to Arduino, let's first walkthrough the board profile setup using the Adafruit Feather M0 Express guide.

Installing Libraries in Arduino

With the board profile, we can then install the dependencies. We'll use Arduino's built-in Library Manage to install the libraries. Goto Sketch > Include Library  and select Manage Libraries. Here, we'll search for the Adafruit VS1053 Library and install the latest version.

 Uploading Code

With the Feather board profile and Arduino library installed, download the mp3player.ino file save them to a new folder named “mp3player”. You'll want to place the new folder into arduino's sketches folder (ie. ~/HD/Documents/Arduino/sketches).

Open the mp3player.ino file in the Arduino IDE. Select the Adafruit Feather M0 Express board under the Tools > Board menu. Connect the Feather board to your computer via microUSB cable and select "/dev/cu.modem..." under the Tools > Port menu (Option might be named different using Windows OS).

Upload the code to the Feather using the Upload command, Cmd+U or clicking the arrow icon.

// SPDX-FileCopyrightText: 2017 Carter Nelson for Adafruit Industries
//
// SPDX-License-Identifier: MIT

//
// Gordon Cole MP3 Player
//

#include <SPI.h>
#include <SD.h>
#include <Adafruit_VS1053.h>

// Guide is based on Feather M0 Express
// ARDUINO_SAMD_FEATHER_M0 defines only
// VS1053 Pins
#define VS1053_RESET   -1     // VS1053 reset pin (not used!)
#define VS1053_CS       6     // VS1053 chip select pin (output)
#define VS1053_DCS      10    // VS1053 Data/command select pin (output)
#define CARDCS          5     // Card chip select pin
#define VS1053_DREQ     9     // VS1053 Data request, ideally an Interrupt pin
// Button Pins
#define BUTTON_PLAY     13    // PLAY / STOP button
#define BUTTON_PAUSE    12    // PAUSE / RESUME button
#define BUTTON_NEXT     11    // NEXT button
// Status LED
#define LED_STATUS      19    // status LED
#define BLINK_RATE      500   // blink rate in ms
// Volume Control
#define KNOB_VOLUME     0     // volume knob
#define KNOB_MIN        0     // min ADC value
#define KNOB_MAX        1023  // max ADC value
#define VOL_MIN         0     // min volume (most loud)
#define VOL_MAX         50    // max volume (most quiet)
#define VOL_UPDATE      250   // update rate in ms
#define VOL_SAMPLES     10    // number of reads for average
#define VOL_SAMPLE_RATE 5     // ms delay per sample
#define VOL_THRESHOLD   20    // vol must change by this many counts
// Maximum number of files (tracks) to load
#define TRACKS_MAX      100
// Player behavior
#define AUTO_PLAY_NEXT  true  // true to automatically go to next track

unsigned long currentMillis;
unsigned long previousBlinkMillis, previousVolMillis;
int currentKnob, previousKnob;
int volume;
int currentTrack, totalTracks;
char trackListing[TRACKS_MAX][13] = {' '};
enum mode {
  PLAYING,
  PAUSED,
  STOPPED
} currentMode = STOPPED;

Adafruit_VS1053_FilePlayer musicPlayer = 
  Adafruit_VS1053_FilePlayer(VS1053_RESET, VS1053_CS, VS1053_DCS, VS1053_DREQ, CARDCS);

//-----------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  // Leave commented for standalone operation, uncomment for troubleshooting
  //while (!Serial) ;

  // Initialize pins
  pinMode(BUTTON_PLAY, INPUT_PULLUP);
  pinMode(BUTTON_PAUSE, INPUT_PULLUP);
  pinMode(BUTTON_NEXT, INPUT_PULLUP);
  pinMode(LED_STATUS, OUTPUT);

  // Initialize status LED
  previousBlinkMillis = millis();
  digitalWrite(LED_STATUS, LOW);

  Serial.println("\n\nGordon Cole MP3 Player");

  // Initialize the music player  
  if (! musicPlayer.begin()) { 
     Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
     while (1) {
       digitalWrite(LED_STATUS, !digitalRead(LED_STATUS));
       delay(100);        
     }
  }
  Serial.println(F("VS1053 found"));
  musicPlayer.softReset();

  // Make a tone to indicate VS1053 is working 
  musicPlayer.sineTest(0x44, 500);    

  // Set volume for left, right channels. lower numbers == louder volume!
  previousVolMillis = millis();  
  previousKnob = analogRead(KNOB_VOLUME);
  volume = map(previousKnob, KNOB_MIN, KNOB_MAX, VOL_MIN, VOL_MAX);
  Serial.print("Volume = "); Serial.println(volume);
  musicPlayer.setVolume(volume, volume);

  // Initialize the SD card
  if (!SD.begin(CARDCS)) {
    Serial.println(F("SD failed, or not present"));
    while (1) {
       digitalWrite(LED_STATUS, !digitalRead(LED_STATUS));
       delay(100);        
    }
  }
  Serial.println("SD OK!");

  // Load list of tracks
  Serial.println("Track Listing");
  Serial.println("=============");  
  totalTracks = 0;
  loadTracks(SD.open("/"), 0);
  currentTrack = 0;

  // Setup interrupts (DREQ) for playback 
  musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT); 
}

//-----------------------------------------------------------------------------
void loop() {
  // Check and set volume
  updateVolume();

  // Update status LED
  updateStatusLED();

  // Auto play next track if feature enabled
  if (AUTO_PLAY_NEXT) {
    if (currentMode==PLAYING && musicPlayer.stopped()) {
      currentTrack = ++currentTrack < totalTracks ? currentTrack : 0; 
      Serial.print("Next ");
      Serial.print(currentTrack); Serial.print("=");
      Serial.println(trackListing[currentTrack]);
      musicPlayer.startPlayingFile(trackListing[currentTrack]);
      currentMode = PLAYING;
    }
  }

  // Start / Stop
  if (!digitalRead(BUTTON_PLAY)) {
    if (musicPlayer.stopped()) {
      Serial.print("Start ");
      Serial.print(currentTrack); Serial.print("=");
      Serial.println(trackListing[currentTrack]);
      musicPlayer.startPlayingFile(trackListing[currentTrack]);
      currentMode = PLAYING;
    } else {
      Serial.println("Stopped.");
      musicPlayer.stopPlaying();
      currentMode = STOPPED;      
    }
    delay(250);
  }

  // Pause / Resume
  if (!digitalRead(BUTTON_PAUSE)) {
    if (!musicPlayer.stopped()) {
      if (musicPlayer.paused()) {
        Serial.println("Resumed");
        musicPlayer.pausePlaying(false);
        currentMode = PLAYING;
      } else { 
        Serial.println("Paused");
        musicPlayer.pausePlaying(true);
        currentMode = PAUSED;
      }    
    }
    delay(250);
  }

  // Next
  if (!digitalRead(BUTTON_NEXT)) {
    if (!musicPlayer.stopped()) {
      Serial.println("Stopping current playback.");
      musicPlayer.stopPlaying();
    }
    currentTrack = ++currentTrack < totalTracks ? currentTrack : 0; 
    Serial.print("Next ");
    Serial.print(currentTrack); Serial.print("=");
    Serial.println(trackListing[currentTrack]);
    musicPlayer.startPlayingFile(trackListing[currentTrack]);
    currentMode = PLAYING;
    delay(250);
  }
}

//-----------------------------------------------------------------------------
void updateVolume() {
  // Rate limit
  currentMillis = millis();
  if (currentMillis - previousVolMillis < VOL_UPDATE) return;
  previousVolMillis = currentMillis;
  // Get an average reading
  currentKnob = 0;
  for (int i=0; i<VOL_SAMPLES; i++) {
    currentKnob += analogRead(KNOB_VOLUME);
    delay(VOL_SAMPLE_RATE);
  }
  currentKnob /= VOL_SAMPLES;
  // Only update if it's changed
  if (abs(currentKnob-previousKnob) > VOL_THRESHOLD) {
    Serial.print("["); Serial.print(currentKnob);
    Serial.print(","); Serial.print(previousKnob);
    Serial.print("] ");
    previousKnob = currentKnob;
    volume = map(currentKnob, KNOB_MIN, KNOB_MAX, VOL_MIN, VOL_MAX);
    Serial.print("Volume set to: "); Serial.println(volume);
    musicPlayer.setVolume(volume, volume);  
  }  
}

//-----------------------------------------------------------------------------
void updateStatusLED() {
  if (musicPlayer.paused()) {
    // Blink it like a polaroid
    currentMillis = millis();
    if (currentMillis - previousBlinkMillis > BLINK_RATE) {
       previousBlinkMillis = currentMillis;
       digitalWrite(LED_STATUS, !digitalRead(LED_STATUS));
    }
  } else if (!musicPlayer.stopped()) {
    // It's so on again
    digitalWrite(LED_STATUS, HIGH);
  } else {
    // It's so off again
    digitalWrite(LED_STATUS, LOW);
  }
}

//-----------------------------------------------------------------------------
void loadTracks(File dir, int level) {
  while (true) {
    File entry = dir.openNextFile();
    if (!entry) return;
    if (entry.isDirectory()) {
      // Recursive call to scan next dir level
      loadTracks(entry, level + 1);
    } else {
      // Only add files in root dir
      if (level == 0) {
        // And only if they have good names
        if (nameCheck(entry.name())) {
          strncpy(trackListing[totalTracks], entry.name(), 12);
          Serial.print(totalTracks); Serial.print("=");
          Serial.println(trackListing[totalTracks]);
          totalTracks++;
        }
      }
    }
    entry.close();
    // Stop scanning if we hit max
    if (totalTracks >= TRACKS_MAX) return;
  } 
}

//-----------------------------------------------------------------------------
bool nameCheck(char* name) {
  int len = strlen(name);
  // Check length
  if (len <= 4) return false;
  // Check extension
  char* ext = strrchr(name,'.');
  if (!(
    strcmp(ext,".MP3") == 0  ||
    strcmp(ext,".OGG") == 0
    )) return false;
  // Check first character
  switch(name[0]) {
    case '_': return false;
  }
  return true;
}

This guide was first published on Oct 25, 2017. It was last updated on Mar 28, 2024.

This page (Code) was last updated on Mar 28, 2024.

Text editor powered by tinymce.