The Arduino platform is designed to be flexible enough to create any number of projects with a single board like the Uno. However, some projects can benefit from specialized boards that are engineered to perform specific tasks. To show how, we’ll build a soundboard out of the LilyPad MP3, a variant Arduino board.
Open source software platforms allow developers to examine and modify their code. In the same way, open source hardware platforms like Arduino allow third-parties to build their own versions of compatible boards. The LilyPad line of Arduino boards is one such variation. This board is designed for e-textiles and can be sewn into fabric projects. This makes it ideal for costumes and smart clothing.
The LilyPad MP3 is a further sub-variant of the LilyPad line that includes a built-in SD card reader, a stereo audio amplifier and 3.5mm headphone jack, and five pre-programmed trigger inputs. Without any coding set up, you can connect up to five triggers to play pre-recorded sound files. If you’ve ever seen the Star Wars stormtroopers at Disney that can play a set of pre-scripted phrases, the LilyPad MP3 can function very similarly.
While the LilyPad MP3 can be programmed just like any Arduino board, the advantage for this project is that you can set up a soundboard without modifying the existing code whatsoever. It also demonstrates how valuable it can be to find the right board for your project early on. Using a regular Arduino Uno would require an external shield to have the same capability as a LilyPad MP3 and would be much bulkier. Since most Arduino boards are compatible with most sketches, finding the form factor that’s right for your needs can save you a lot of time and hassle.
For this project, we’ll be demonstrating how the LilyPad MP3 works on a breadboard, just to get the wiring working. To get started, you’ll need a LilyPad MP3.
You’ll also need from 1-5 buttons (one is required, but the LilyPad supports up to five), a speaker (or headphones if you prefer), and a 3.5 mm cable
Since the LilyPad is designed to be used in clothing, you can optionally use some thread and needles to sew the device into a coat, costume, or anything else where you’d like to have a soundboard. Conductive thread can allow you to weave wiring directly into the fabric of your clothes.
As mentioned above, the code for the default Trigger sketch on the LilyPad MP3 comes preloaded on every board. You don’t need to upload this, but it is helpful to take a look through the code to get an understanding of how it works.
// “Trigger” example sketch for Lilypad MP3 Player
// Mike Grusin, SparkFun Electronics
// http://www.sparkfun.com
// This sketch (which is preloaded onto the board by default),
// will play a specific audio file when one of the five trigger
// inputs (labeled T1 – T5) is momentarily grounded.
// You can place up to five audio files on the micro-SD card.
// These files should have the desired trigger number (1 to 5)
// as the first character in the filename. The rest of the
// filename can be anything you like. Long file names will work,
// but will be translated into short 8.3 names. We recommend using
// 8.3 format names without spaces, but the following characters
// are OK: .$%’-_@~`!(){}^#&. The VS1053 can play a variety of
// audio formats, see the datasheet for information.
// By default, a new trigger will interrupt a playing file, except
// itself. (In other words, a new trigger won’t restart an
// already-playing file). You can easily change this behavior by
// modifying the global variables “interrupt” and “interruptself”
// below.
// This sketch can output serial debugging information if desired
// by changing the global variable “debugging” to true. Note that
// this will take away trigger inputs 4 and 5, which are shared
// with the TX and RX lines. You can keep these lines connected to
// trigger switches and use the serial port as long as the triggers
// are normally open (not grounded) and remain ungrounded while the
// serial port is in use.
// Uses the SdFat library by William Greiman, which is supplied
// with this archive, or download from http://code.google.com/p/sdfatlib/
// Uses the SFEMP3Shield library by Bill Porter, which is supplied
// with this archive, or download from http://www.billporter.info/
// License:
// We use the “beerware” license for our firmware. You can do
// ANYTHING you want with this code. If you like it, and we meet
// someday, you can, but are under no obligation to, buy me a
// (root) beer in return.
// Have fun!
// -your friends at SparkFun
// Revision history:
// 1.0 initial release MDG 2012/11/01
// We’ll need a few libraries to access all this hardware!
#include // To talk to the SD card and MP3 chip
#include // SD card file system
#include // MP3 decoder chip
// Constants for the trigger input pins, which we’ll place
// in an array for convenience:
const int TRIG1 = A0;
const int TRIG2 = A4;
const int TRIG3 = A5;
const int TRIG4 = 1;
const int TRIG5 = 0;
int trigger[5] = {TRIG1, TRIG2, TRIG3, TRIG4, TRIG5};
// And a few outputs we’ll be using:
const int ROT_LEDR = 10; // Red LED in rotary encoder (optional)
const int EN_GPIO1 = A2; // Amp enable + MIDI/MP3 mode select
const int SD_CS = 9; // Chip Select for SD card
// Create library objects:
SFEMP3Shield MP3player;
SdFat sd;
// Set debugging = true if you’d like status messages sent
// to the serial port. Note that this will take over trigger
// inputs 4 and 5. (You can leave triggers connected to 4 and 5
// and still use the serial port, as long as you’re careful to
// NOT ground the triggers while you’re using the serial port).
boolean debugging = false;
// Set interrupt = false if you would like a triggered file to
// play all the way to the end. If this is set to true, new
// triggers will stop the playing file and start a new one.
boolean interrupt = true;
// Set interruptself = true if you want the above rule to also
// apply to the same trigger. In other words, if interrupt = true
// and interruptself = false, subsequent triggers on the same
// file will NOT start the file over. However, a different trigger
// WILL stop the original file and start a new one.
boolean interruptself = false;
// We’ll store the five filenames as arrays of characters.
// “Short” (8.3) filenames are used, followed by a null character.
char filename[5][13];
void setup()
{
int x, index;
SdFile file;
byte result;
char tempfilename[13];
// Set the five trigger pins as inputs, and turn on the
// internal pullup resistors:
for (x = 0; x <= 4; x++) { pinMode(trigger[x], INPUT); digitalWrite(trigger[x], HIGH); } // If serial port debugging is inconvenient, you can connect // a LED to the red channel of the rotary encoder to blink // startup error codes: pinMode(ROT_LEDR, OUTPUT); digitalWrite(ROT_LEDR, HIGH); // HIGH = off // The board uses a single I/O pin to select the // mode the MP3 chip will start up in (MP3 or MIDI), // and to enable/disable the amplifier chip: pinMode(EN_GPIO1, OUTPUT); digitalWrite(EN_GPIO1, LOW); // MP3 mode / amp off // If debugging is true, initialize the serial port: // (The ‘F’ stores constant strings in flash memory to save RAM) if (debugging) { Serial.begin(9600); Serial.println(F(“Lilypad MP3 Player trigger sketch”)); } // Initialize the SD card; SS = pin 9, half speed at first if (debugging) Serial.print(F(“initialize SD card… “)); result = sd.begin(SD_CS, SPI_HALF_SPEED); // 1 for success if (result != 1) // Problem initializing the SD card { if (debugging) Serial.print(F(“error, halting”)); errorBlink(1); // Halt forever, blink LED if present. } else if (debugging) Serial.println(F(“success!”)); // Start up the MP3 library if (debugging) Serial.print(F(“initialize MP3 chip… “)); result = MP3player.begin(); // 0 or 6 for success // Check the result, see the library readme for error codes. if ((result != 0) && (result != 6)) // Problem starting up { if (debugging) { Serial.print(F(“error code “)); Serial.print(result); Serial.print(F(“, halting.”)); } errorBlink(result); // Halt forever, blink red LED if present. } else if (debugging) Serial.println(F(“success!”)); // Now we’ll access the SD card to look for any (audio) files // starting with the characters ‘1’ to ‘5’: if (debugging) Serial.println(F(“reading root directory”)); // Start at the first file in root and step through all of them: sd.chdir(“/”, true); while (file.openNext(sd.vwd(), O_READ)) { // get filename file.getFilename(tempfilename); // Does the filename start with char ‘1’ through ‘5’? if (tempfilename[0] >= ‘1’ && tempfilename[0] <= ‘5’)
{
// Yes! subtract char ‘1’ to get an index of 0 through 4.
index = tempfilename[0] – ‘1’;
// Copy the data to our filename array.
strcpy(filename[index], tempfilename);
if (debugging) // Print out file number and name
{
Serial.print(F(“found a file with a leading “));
Serial.print(index + 1);
Serial.print(F(“: “));
Serial.println(filename[index]);
}
}
else if (debugging)
{
Serial.print(F(“found a file w/o a leading number: “));
Serial.println(tempfilename);
}
file.close();
}
if (debugging)
Serial.println(F(“done reading root directory”));
if (debugging) // List all the files we saved:
{
for(x = 0; x <= 4; x++)
{
Serial.print(F(“trigger “));
Serial.print(x + 1);
Serial.print(F(“: “));
Serial.println(filename[x]);
}
}
// Set the VS1053 volume. 0 is loudest, 255 is lowest (off):
MP3player.setVolume(10, 10);
// Turn on the amplifier chip:
digitalWrite(EN_GPIO1, HIGH);
delay(2);
}
void loop()
{
int t; // current trigger
static int last_t; // previous (playing) trigger
int x;
byte result;
// Step through the trigger inputs, looking for LOW signals.
// The internal pullup resistors will keep them HIGH when
// there is no connection to the input.
// If serial debugging is on, only check triggers 1-3,
// otherwise check triggers 1-5.
for(t = 1; t <= (debugging ? 3 : 5); t++)
{
// The trigger pins are stored in the inputs[] array.
// Read the pin and check if it is LOW (triggered).
if (digitalRead(trigger[t – 1]) == LOW)
{
// Wait for trigger to return high for a solid 50ms
// (necessary to avoid switch bounce on T2 and T3
// since we need those free for I2C control of the
// amplifier)
x = 0;
while(x < 50)
{
if (digitalRead(trigger[t – 1]) == HIGH)
x++;
else
x = 0;
delay(1);
}
if (debugging)
{
Serial.print(F(“got trigger “));
Serial.println(t);
}
// Do we have a valid filename for this trigger?
// (Invalid filenames will have 0 as the first character)
if (filename[t – 1][0] == 0)
{
if (debugging)
Serial.println(F(“no file with that number”));
}
else // We do have a filename for this trigger!
{
// If a file is already playing, and we’ve chosen to
// allow playback to be interrupted by a new trigger,
// stop the playback before playing the new file.
if (interrupt && MP3player.isPlaying() && ((t != last_t) || interruptself))
{
if (debugging)
Serial.println(F(“stopping playback”));
MP3player.stopTrack();
}
// Play the filename associated with the trigger number.
// (If a file is already playing, this command will fail
// with error #2).
result = MP3player.playMP3(filename[t – 1]);
if (result == 0) last_t = t; // Save playing trigger
if(debugging)
{
if(result != 0)
{
Serial.print(F(“error “));
Serial.print(result);
Serial.print(F(” when trying to play track “));
}
else
{
Serial.print(F(“playing “));
}
Serial.println(filename[t – 1]);
}
}
}
}
}
void errorBlink(int blinks)
{
// The following function will blink the red LED in the rotary
// encoder (optional) a given number of times and repeat forever.
// This is so you can see any startup error codes without having
// to use the serial monitor window.
int x;
while(true) // Loop forever
{
for (x = 0; x < blinks; x++) // Blink the given number of times
{
digitalWrite(ROT_LEDR, LOW); // Turn LED ON
delay(250);
digitalWrite(ROT_LEDR, HIGH); // Turn LED OFF
delay(250);
}
delay(1500); // Longer pause between blink-groups
}
}
While there’s too much code to go through line by line (and it’s not necessary to make this project work) there are a couple of key points worth highlighting. First, there are a few functions that are part of Bill Porter’s MP3 Player library. These functions control playing audio files through the VS1053B decoder chip. The unique functions include
● setVolume(): This function can adjust the output volume level.
● isPlaying(): This function can be used to determine whether or not an MP3 is currently playing (useful for interrupting or stopping playback).
● stopTrack(): This can stop a track currently in playback.
● playMP3(): This function plays a file from the SD card. The default sketch is set to play a file that matches the trigger that the input is attached to.
To set up audio tracks on the LilyPad, you can add up to five audio files to a micro SD card. Despite the MP3 in the LilyPad’s name, it supports a wide variety of audio file formats. You can find a list of them here. You can name the files whatever you want, as long as you start the file name with a number from 1 to 5.
The default sketch will play an audio file that corresponds with the trigger that gets activated. Trigger 1 (labeled T1 on the LilyPad) will activate an audio file that starts with 1. Trigger 2 will play the file that starts with 2 and so on. As long as you name your audio files correctly, you don’t have to deal with any of the coding.
Connecting all the components to the LilyPad is simple. Plug the micro SD card into the micro SD card slot and plug a speaker or pair of headphones into the headphone jack. The LilyPad also has positive and negative contacts for a left and right speaker if you’d rather wire a speaker directly to the board.
Next, connect a button to each trigger you want to use. One side of the button goes to GND, while the other goes to the trigger contact you want to activate. The trigger contacts on the LilyPad feature one large circular contact and a smaller one right next to the big one. You can solder wires to the smaller contact or wrap wiring around the larger contact.
Press the button and it should trigger the audio file playback. Once you know that the wiring works, you can try sewing the wiring into fabric using conductive wiring.