Arduino Personal Soundtrack Hoodie
by Jonathan Robson in Circuits > Arduino
3688 Views, 15 Favorites, 0 Comments
Arduino Personal Soundtrack Hoodie
This was inspired by Raj's personal soundtrack shirt in Big Bang Theory S3, E16, "The Excelsior Acquisition". You can now buy them online but it's much more fun to make your own and it's possible now that buttons designed for use in electronic clothing are available from Connectedwear.
Nine of the buttons on the Connectedwear keypad sewn into the hoodie's arms each play a given sound or tune and the tenth accesses a randomized library of small-boy friendly sound effects including farts, belches, screams, cheering, crashes, explosions, sirens etc.
The Connectedwear keypads are designed to operate an iPod but in a short email exchange they explained that the buttons work by each having a separate resistance built in. I used the Arduino Analog Read sketch from the Arduino website to read the resistance thru a 10K pull-up resistor when each button is pressed. I then used one of the several "multiple buttons on one analog pin" tutorials available on the web to build that code into the Waveshield code from the AdaFruit website tutorials.
Nine of the buttons on the Connectedwear keypad sewn into the hoodie's arms each play a given sound or tune and the tenth accesses a randomized library of small-boy friendly sound effects including farts, belches, screams, cheering, crashes, explosions, sirens etc.
The Connectedwear keypads are designed to operate an iPod but in a short email exchange they explained that the buttons work by each having a separate resistance built in. I used the Arduino Analog Read sketch from the Arduino website to read the resistance thru a 10K pull-up resistor when each button is pressed. I then used one of the several "multiple buttons on one analog pin" tutorials available on the web to build that code into the Waveshield code from the AdaFruit website tutorials.
Parts
Parts:
Arduino (make sure it's compatible with a Waveshield - some aren't)
Waveshield
2x Connectedwear Lite keypad part# KP2-4G from http://www.fibretronic.com
2x 2.5mm audio mono line sockets
2x 10K resistors
5cm, 8ohm speaker (Visaton K50WP)
9v battery connector with switch
Stripboard
Jumper wires
Arduino (make sure it's compatible with a Waveshield - some aren't)
Waveshield
2x Connectedwear Lite keypad part# KP2-4G from http://www.fibretronic.com
2x 2.5mm audio mono line sockets
2x 10K resistors
5cm, 8ohm speaker (Visaton K50WP)
9v battery connector with switch
Stripboard
Jumper wires
Circuit:
Breadboard everything first to read the resistance when each button is pressed. From the 2.5mm jack sockets put the center connection into ground via 10K resistor , with a pull up jumper into analog 0 pin on the Arduino. Connect either of the side pins on the 2.5mm socket to 5v pin. Repeat for the other 2.5mm socket with ascend resistor onto analog 1 pin. Connect a 9v battery to GND and the Vin pin thru any type of on/off switch you have available. Circuit diagrams are not my strong point but withe the help of Fritzing, here goes. Hope it makes sense.
Breadboard everything first to read the resistance when each button is pressed. From the 2.5mm jack sockets put the center connection into ground via 10K resistor , with a pull up jumper into analog 0 pin on the Arduino. Connect either of the side pins on the 2.5mm socket to 5v pin. Repeat for the other 2.5mm socket with ascend resistor onto analog 1 pin. Connect a 9v battery to GND and the Vin pin thru any type of on/off switch you have available. Circuit diagrams are not my strong point but withe the help of Fritzing, here goes. Hope it makes sense.
Calibrate the Buttons
Calibrate:
Plug in the keypads to the 2.5mm audio sockets and load the Analog_Read sketch from the Arduino website:
int analogPin = 0;
int val = 0; // variable to store the value read
void setup()
{
Serial.begin(9600); // setup serial
}
void loop()
{
val = analogRead(analogPin); // read the input pin
Serial.println(val); // debug value
}
Read the input each time a button is pressed on pin 0 then reload the sketch, having changed int analogPin to 1. It's important read both sets of buttons separately as they all seem to be slightly different to each other.
Make sure all your jumpers are firm as a wobbly lead will have your readings bouncing all over the place. You know the circuit is solid when the serial monitor gives you a nice long line of zeros between button presses. Do this with your 9v power supply connected and switched on to replicate the system current in the final setup.
Plug in the keypads to the 2.5mm audio sockets and load the Analog_Read sketch from the Arduino website:
int analogPin = 0;
int val = 0; // variable to store the value read
void setup()
{
Serial.begin(9600); // setup serial
}
void loop()
{
val = analogRead(analogPin); // read the input pin
Serial.println(val); // debug value
}
Read the input each time a button is pressed on pin 0 then reload the sketch, having changed int analogPin to 1. It's important read both sets of buttons separately as they all seem to be slightly different to each other.
Make sure all your jumpers are firm as a wobbly lead will have your readings bouncing all over the place. You know the circuit is solid when the serial monitor gives you a nice long line of zeros between button presses. Do this with your 9v power supply connected and switched on to replicate the system current in the final setup.
Audio Files
Load your audio files onto the SD card on the Waveshield. Because I wanted as small a speakers possible to reduce weight and size but as loud a volume as possible I used Final Cut to boost the sound output by dragging the Volume Output Overlay to 11db for each sound file then saved using the Quicktime Conversion option. Sound files will only play if they fit the parameters laid out on http://www.ladyada.net/make/waveshield/. I used .wav, Linear PMC, Mono, 22Khz, Best Output, 8 Bits as my settings. The files distort a little coming out of the 5cm 8ohm speaker, but not too much.
Code
The bulk of the code is for the WaveShield, based on http://www.ladyada.net/make/waveshield/
There are several multi-button examples around- I used a mash-up based on Rayshobby blog http://rayshobby.net/?p=16 with a bit of this one, https://www.instructables.com/id/Arduino-iPod/step4/Download-the-program/ thrown in.
Random selection code for the last button is adapted from www.arduino.cc/en/Tutorial/Blink & www.arduino.cc/en/Reference/Random.
#include <FatReader.h>
#include <SdReader.h>
#include <avr/pgmspace.h>
#include "WaveUtil.h"
#include "WaveHC.h"
int buttonPinR = 0; // Right arm buttons on Analog pin 0
int buttonValueR = 0;
int buttonPinL = 1; // Left arm buttons on Analog pin 1
int buttonValueL = 0;
long randChoose = 0; // Initialize random element
long randNumber;
SdReader card; // This object holds the information for the card
FatVolume vol; // This holds the information for the partition on the card
FatReader root; // This holds the information for the filesystem on the card
FatReader f; // This holds the information for the file we're play
WaveHC wave; // This is the only wave (audio) object, since we will only play one at a time
int wasplaying = 0;
int rVal;
// this handy function will return the number of bytes currently free in RAM, great for debugging!
int freeRam(void)
{
extern int __bss_end;
extern int *__brkval;
int free_memory;
if((int)__brkval == 0) {
free_memory = ((int)&free_memory) - ((int)&__bss_end);
}
else {
free_memory = ((int)&free_memory) - ((int)__brkval);
}
return free_memory;
}
void sdErrorCheck(void)
{
if (!card.errorCode()) return;
putstring("\n\rSD I/O error: ");
Serial.print(card.errorCode(), HEX);
putstring(", ");
Serial.println(card.errorData(), HEX);
while(1);
}
void setup()
{
Serial.begin(9600);
Serial.println("Wave test!");
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(13, OUTPUT);
randomSeed (analogRead (1)); // setup random number generator
// if (!card.init(true)) { //play with 4 MHz spi if 8MHz isn't working for you
if (!card.init()) { //play with 8 MHz spi (default faster!)
putstring_nl("Card init. failed!"); // Something went wrong, lets print out why
sdErrorCheck();
while(1); // then 'halt' - do nothing!
}
// enable optimize read - some cards may timeout. Disable if you're having problems
card.partialBlockRead(true);
// Now we will look for a FAT partition!
uint8_t part;
for (part = 0; part < 5; part++) { // we have up to 5 slots to look in
if (vol.init(card, part))
break; // we found one, lets bail
}
if (part == 5) { // if we ended up not finding one :(
putstring_nl("No valid FAT partition!");
sdErrorCheck(); // Something went wrong, lets print out why
while(1); // then 'halt' - do nothing!
}
// Lets tell the user about what we found
putstring("Using partition ");
Serial.print(part, DEC);
putstring(", type is FAT");
Serial.println(vol.fatType(),DEC); // FAT16 or FAT32?
// Try to open the root directory
if (!root.openRoot(vol)) {
putstring_nl("Can't open root dir!"); // Something went wrong,
while(1); // then 'halt' - do nothing!
}
// Whew! We got past the tough parts.
putstring_nl("Ready!");
}
// Buttons calibrated with a 10K resistor on the 5v pin with a 9v battery connected to the Arduino power plug.
// Use the values from your analog read sketch to bracket the value assigned to each button. Yours may vary from the ones assigned here.
// Right arm buttons on pin 0
void loop() {
buttonValueR = analogRead(buttonPinR);
if (buttonValueR >645 && buttonValueR<660) {
playcomplete("R1.WAV");
}
delay(25); // It's always good to slow things down a bit when reading inputs
if (buttonValueR >225 && buttonValueR<240) {
playcomplete("R2.WAV");
}
delay(25);
if (buttonValueR >165 && buttonValueR<185) {
playcomplete("R3.WAV");
}
delay(25);
if (buttonValueR >325 && buttonValueR<345) {
playcomplete("R4.WAV");
}
delay(25);
if (buttonValueR >495 && buttonValueR<515) {
playcomplete("R5.WAV");
}
// Left arm buttons on pin 1
buttonValueL = analogRead(buttonPinL);
if (buttonValueL >645 && buttonValueL<660) {
playcomplete("L1.WAV");
}
delay(25);
if (buttonValueL >225 && buttonValueL<245) {
playcomplete("L2.WAV");
}
delay(25);
if (buttonValueL >165 && buttonValueL<185) {
playcomplete("L3.WAV");
}
delay(25);
if (buttonValueL >325 && buttonValueL<350) {
playcomplete("L4.WAV");
}
delay(25);
// Randomize the sound affect played when the last button on the left arm is pressed
if (buttonValueL >495 && buttonValueL<515) {
randChoose = random (1, 20); // generate a choice between 1 and 20
switch (randChoose)
{
case 1:
playcomplete("RAND1.WAV");
break;
case 2:
playcomplete("RAND2.WAV");
break;
case 3:
playcomplete("RAND3.WAV");
break;
case 4:
playcomplete("RAND4.WAV");
break;
case 5:
playcomplete("RAND5.WAV");
break;
case 6:
playcomplete("RAND6.WAV");
break;
case 7:
playcomplete("RAND7.WAV");
break;
case 8:
playcomplete("RAND8.WAV");
break;
case 9:
playcomplete("RAND9.WAV");
break;
case 10:
playcomplete("RAND10.WAV");
break;
case 11:
playcomplete("RAND11.WAV");
break;
case 12:
playcomplete("RAND12.WAV");
break;
case 13:
playcomplete("RAND13.WAV");
break;
case 14:
playcomplete("RAND14.WAV");
break;
case 15:
playcomplete("RAND15.WAV");
break;
case 16:
playcomplete("RAND16.WAV");
break;
case 17:
playcomplete("RAND17.WAV");
break;
case 18:
playcomplete("RAND18.WAV");
break;
case 19:
playcomplete("RAND19.WAV");
break;
case 20:
playcomplete("RAND20.WAV");
break;
}
delay(25);
}
}
// Plays a full file from beginning to end with no pause.
void playcomplete(char *name) {
// call our helper to find and play this name
playfile(name);
while (wave.isplaying) {
// do nothing while its playing
}
// now its done playing
}
void playfile(char *name) {
// see if the wave object is currently doing something
if (wave.isplaying) {// already playing something, so stop it!
wave.stop(); // stop it
}
// look in the root directory and open the file
if (!f.open(root, name)) {
putstring("Couldn't open file "); Serial.print(name); return;
}
// OK read the file and turn it into a wave object
if (!wave.create(f)) {
putstring_nl("Not a valid WAV"); return;
}
// ok time to play! start playback
wave.play();
}
There are several multi-button examples around- I used a mash-up based on Rayshobby blog http://rayshobby.net/?p=16 with a bit of this one, https://www.instructables.com/id/Arduino-iPod/step4/Download-the-program/ thrown in.
Random selection code for the last button is adapted from www.arduino.cc/en/Tutorial/Blink & www.arduino.cc/en/Reference/Random.
#include <FatReader.h>
#include <SdReader.h>
#include <avr/pgmspace.h>
#include "WaveUtil.h"
#include "WaveHC.h"
int buttonPinR = 0; // Right arm buttons on Analog pin 0
int buttonValueR = 0;
int buttonPinL = 1; // Left arm buttons on Analog pin 1
int buttonValueL = 0;
long randChoose = 0; // Initialize random element
long randNumber;
SdReader card; // This object holds the information for the card
FatVolume vol; // This holds the information for the partition on the card
FatReader root; // This holds the information for the filesystem on the card
FatReader f; // This holds the information for the file we're play
WaveHC wave; // This is the only wave (audio) object, since we will only play one at a time
int wasplaying = 0;
int rVal;
// this handy function will return the number of bytes currently free in RAM, great for debugging!
int freeRam(void)
{
extern int __bss_end;
extern int *__brkval;
int free_memory;
if((int)__brkval == 0) {
free_memory = ((int)&free_memory) - ((int)&__bss_end);
}
else {
free_memory = ((int)&free_memory) - ((int)__brkval);
}
return free_memory;
}
void sdErrorCheck(void)
{
if (!card.errorCode()) return;
putstring("\n\rSD I/O error: ");
Serial.print(card.errorCode(), HEX);
putstring(", ");
Serial.println(card.errorData(), HEX);
while(1);
}
void setup()
{
Serial.begin(9600);
Serial.println("Wave test!");
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(13, OUTPUT);
randomSeed (analogRead (1)); // setup random number generator
// if (!card.init(true)) { //play with 4 MHz spi if 8MHz isn't working for you
if (!card.init()) { //play with 8 MHz spi (default faster!)
putstring_nl("Card init. failed!"); // Something went wrong, lets print out why
sdErrorCheck();
while(1); // then 'halt' - do nothing!
}
// enable optimize read - some cards may timeout. Disable if you're having problems
card.partialBlockRead(true);
// Now we will look for a FAT partition!
uint8_t part;
for (part = 0; part < 5; part++) { // we have up to 5 slots to look in
if (vol.init(card, part))
break; // we found one, lets bail
}
if (part == 5) { // if we ended up not finding one :(
putstring_nl("No valid FAT partition!");
sdErrorCheck(); // Something went wrong, lets print out why
while(1); // then 'halt' - do nothing!
}
// Lets tell the user about what we found
putstring("Using partition ");
Serial.print(part, DEC);
putstring(", type is FAT");
Serial.println(vol.fatType(),DEC); // FAT16 or FAT32?
// Try to open the root directory
if (!root.openRoot(vol)) {
putstring_nl("Can't open root dir!"); // Something went wrong,
while(1); // then 'halt' - do nothing!
}
// Whew! We got past the tough parts.
putstring_nl("Ready!");
}
// Buttons calibrated with a 10K resistor on the 5v pin with a 9v battery connected to the Arduino power plug.
// Use the values from your analog read sketch to bracket the value assigned to each button. Yours may vary from the ones assigned here.
// Right arm buttons on pin 0
void loop() {
buttonValueR = analogRead(buttonPinR);
if (buttonValueR >645 && buttonValueR<660) {
playcomplete("R1.WAV");
}
delay(25); // It's always good to slow things down a bit when reading inputs
if (buttonValueR >225 && buttonValueR<240) {
playcomplete("R2.WAV");
}
delay(25);
if (buttonValueR >165 && buttonValueR<185) {
playcomplete("R3.WAV");
}
delay(25);
if (buttonValueR >325 && buttonValueR<345) {
playcomplete("R4.WAV");
}
delay(25);
if (buttonValueR >495 && buttonValueR<515) {
playcomplete("R5.WAV");
}
// Left arm buttons on pin 1
buttonValueL = analogRead(buttonPinL);
if (buttonValueL >645 && buttonValueL<660) {
playcomplete("L1.WAV");
}
delay(25);
if (buttonValueL >225 && buttonValueL<245) {
playcomplete("L2.WAV");
}
delay(25);
if (buttonValueL >165 && buttonValueL<185) {
playcomplete("L3.WAV");
}
delay(25);
if (buttonValueL >325 && buttonValueL<350) {
playcomplete("L4.WAV");
}
delay(25);
// Randomize the sound affect played when the last button on the left arm is pressed
if (buttonValueL >495 && buttonValueL<515) {
randChoose = random (1, 20); // generate a choice between 1 and 20
switch (randChoose)
{
case 1:
playcomplete("RAND1.WAV");
break;
case 2:
playcomplete("RAND2.WAV");
break;
case 3:
playcomplete("RAND3.WAV");
break;
case 4:
playcomplete("RAND4.WAV");
break;
case 5:
playcomplete("RAND5.WAV");
break;
case 6:
playcomplete("RAND6.WAV");
break;
case 7:
playcomplete("RAND7.WAV");
break;
case 8:
playcomplete("RAND8.WAV");
break;
case 9:
playcomplete("RAND9.WAV");
break;
case 10:
playcomplete("RAND10.WAV");
break;
case 11:
playcomplete("RAND11.WAV");
break;
case 12:
playcomplete("RAND12.WAV");
break;
case 13:
playcomplete("RAND13.WAV");
break;
case 14:
playcomplete("RAND14.WAV");
break;
case 15:
playcomplete("RAND15.WAV");
break;
case 16:
playcomplete("RAND16.WAV");
break;
case 17:
playcomplete("RAND17.WAV");
break;
case 18:
playcomplete("RAND18.WAV");
break;
case 19:
playcomplete("RAND19.WAV");
break;
case 20:
playcomplete("RAND20.WAV");
break;
}
delay(25);
}
}
// Plays a full file from beginning to end with no pause.
void playcomplete(char *name) {
// call our helper to find and play this name
playfile(name);
while (wave.isplaying) {
// do nothing while its playing
}
// now its done playing
}
void playfile(char *name) {
// see if the wave object is currently doing something
if (wave.isplaying) {// already playing something, so stop it!
wave.stop(); // stop it
}
// look in the root directory and open the file
if (!f.open(root, name)) {
putstring("Couldn't open file "); Serial.print(name); return;
}
// OK read the file and turn it into a wave object
if (!wave.create(f)) {
putstring_nl("Not a valid WAV"); return;
}
// ok time to play! start playback
wave.play();
}
Build It
Once you are happy, solder the circuit onto a piece of stripboard and fit it into a suitable enclosure. Find a suitable garment - a hood works great because of the large pocket in the front. Find somebody with a sewing machine to fit it into the hoodie and away you go.