#410007091 kayuli (mel) software code
TRANSCRIPT
All of my software code
(Included code to test mp3 player shield, force sensitive resistor – square, WiFly Shield, basic
16x2 character LCD and codes that combined all code)
Step 1:
Code to test the mp3
player shield
/*
4-28-2011
Spark Fun Electronics 2011
Nathan Seidle
This code is public domain but you buy me a beer if you use
this and we meet someday (Beerware license).
This example code plays a MP3 from the SD card called
'track001.mp3'. The theory is that you can load a
microSD card up with a bunch of MP3s and then play a given
'track' depending on some sort of input such
as which pin is pulled low.
It relies on the sdfatlib from Bill Greiman:
http://code.google.com/p/sdfatlib/
You will need to download and install his library. To compile,
you MUST change Sd2PinMap.h of the SDfatlib!
The default SS_PIN = 10;. You must change this line under the
ATmega328/Arduino area of code to
uint8_t const SS_PIN = 9;. This will cause the sdfatlib to use
pin 9 as the 'chip select' for the
microSD card on pin 9 of the Arduino so that the layout of the
shield works.
Attach the shield to an Arduino. Load code (after editing
Sd2PinMap.h) then open the terminal at 57600bps. This
example shows that it takes ~30ms to load up the VS1053
buffer. We can then do whatever we want for ~100ms
before we need to return to filling the buffer (for another 30ms).
This code is heavily based on the example code I wrote to
control the MP3 shield found here:
http://www.sparkfun.com/products/9736
This example code extends the previous example by reading
the MP3 from an SD card and file rather than from internal
memory of the ATmega. Because the current MP3 shield does
not have a microSD socket, you will need to add the microSD
shield to your Arduino stack.
The main gotcha from all of this is that you have to make sure
your CS pins for each device on an SPI bus is carefully
declared. For the SS pin (aka CS) on the SD FAT libaray, you
need to correctly set it within Sd2PinMap.h. The default
pin in Sd2PinMap.h is 10. If you're using the SparkFun
microSD shield with the SparkFun MP3 shield, the SD CS pin
is pin 9.
Four pins are needed to control the VS1503:
DREQ
CS
DCS
Reset (optional but good to have access to)
Plus the SPI bus
Only the SPI bus pins and another CS pin are needed to
control the microSD card.
What surprised me is the fact that with a normal MP3 we can
do other things for up to 100ms while the MP3 IC crunches
through it's fairly large buffer of 2048 bytes. As long as you
keep your sensor checks or serial reporting to under
100ms and leave ~30ms to then replenish the MP3 buffer, you
can do quite a lot while the MP3 is playing glitch free.
*/
#include <SPI.h>
//Add the SdFat Libraries
#include <SdFat.h>
#include <SdFatUtil.h>
//Create the variables to be used by SdFat Library
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile track;
//This is the name of the file on the microSD card you would like
to play
//Stick with normal 8.3 nomeclature. All lower-case works well.
//Note: you must name the tracks on the SD card with 001, 002,
003, etc.
//For example, the code is expecting to play 'track002.mp3', not
track2.mp3.
char trackName[] = "track001.mp3";
int trackNumber = 1;
char errorMsg[100]; //This is a generic array used for sprintf of
error messages
#define TRUE 0
#define FALSE 1
//MP3 Player Shield pin mapping. See the schematic
#define MP3_XCS 6 //Control Chip Select Pin (for accessing
SPI Control/Status registers)
#define MP3_XDCS 7 //Data Chip Select / BSYNC Pin
#define MP3_DREQ 2 //Data Request Pin: Player asks for
more data
#define MP3_RESET 8 //Reset is active low
//Remember you have to edit the Sd2PinMap.h of the sdfatlib
library to correct control the SD card.
//VS10xx SCI Registers
#define SCI_MODE 0x00
#define SCI_STATUS 0x01
#define SCI_BASS 0x02
#define SCI_CLOCKF 0x03
#define SCI_DECODE_TIME 0x04
#define SCI_AUDATA 0x05
#define SCI_WRAM 0x06
#define SCI_WRAMADDR 0x07
#define SCI_HDAT0 0x08
#define SCI_HDAT1 0x09
#define SCI_AIADDR 0x0A
#define SCI_VOL 0x0B
#define SCI_AICTRL0 0x0C
#define SCI_AICTRL1 0x0D
#define SCI_AICTRL2 0x0E
#define SCI_AICTRL3 0x0F
void setup() {
pinMode(MP3_DREQ, INPUT);
pinMode(MP3_XCS, OUTPUT);
pinMode(MP3_XDCS, OUTPUT);
pinMode(MP3_RESET, OUTPUT);
digitalWrite(MP3_XCS, HIGH); //Deselect Control
digitalWrite(MP3_XDCS, HIGH); //Deselect Data
digitalWrite(MP3_RESET, LOW); //Put VS1053 into hardware
reset
Serial.begin(57600); //Use serial for debugging
Serial.println("MP3 Testing");
//Setup SD card interface
pinMode(10, OUTPUT); //Pin 10 must be set as an output
for the SD communication to work.
if (!card.init(SPI_FULL_SPEED)) Serial.println("Error: Card
init"); //Initialize the SD card and configure the I/O pins.
if (!volume.init(&card)) Serial.println("Error: Volume ini");
//Initialize a volume on the SD card.
if (!root.openRoot(&volume)) Serial.println("Error: Opening
root"); //Open the root directory in the volume.
//We have no need to setup SPI for VS1053 because this has
already been done by the SDfatlib
//From page 12 of datasheet, max SCI reads are CLKI/7. Input
clock is 12.288MHz.
//Internal clock multiplier is 1.0x after power up.
//Therefore, max SPI speed is 1.75MHz. We will use 1MHz to
be safe.
SPI.setClockDivider(SPI_CLOCK_DIV16); //Set SPI bus
speed to 1MHz (16MHz / 16 = 1MHz)
SPI.transfer(0xFF); //Throw a dummy byte at the bus
//Initialize VS1053 chip
delay(10);
digitalWrite(MP3_RESET, HIGH); //Bring up VS1053
//delay(10); //We don't need this delay because any register
changes will check for a high DREQ
//Mp3SetVolume(20, 20); //Set initial volume (20 = -10dB)
LOUD
Mp3SetVolume(40, 40); //Set initial volume (20 = -10dB)
Manageable
//Mp3SetVolume(80, 80); //Set initial volume (20 = -10dB)
More quiet
//Let's check the status of the VS1053
int MP3Mode = Mp3ReadRegister(SCI_MODE);
int MP3Status = Mp3ReadRegister(SCI_STATUS);
int MP3Clock = Mp3ReadRegister(SCI_CLOCKF);
Serial.print("SCI_Mode (0x4800) = 0x");
Serial.println(MP3Mode, HEX);
Serial.print("SCI_Status (0x48) = 0x");
Serial.println(MP3Status, HEX);
int vsVersion = (MP3Status >> 4) & 0x000F; //Mask out only
the four version bits
Serial.print("VS Version (VS1053 is 4) = ");
Serial.println(vsVersion, DEC); //The 1053B should respond
with 4. VS1001 = 0, VS1011 = 1, VS1002 = 2, VS1003 = 3
Serial.print("SCI_ClockF = 0x");
Serial.println(MP3Clock, HEX);
//Now that we have the VS1053 up and running, increase the
internal clock multiplier and up our SPI rate
Mp3WriteRegister(SCI_CLOCKF, 0x60, 0x00); //Set multiplier
to 3.0x
//From page 12 of datasheet, max SCI reads are CLKI/7. Input
clock is 12.288MHz.
//Internal clock multiplier is now 3x.
//Therefore, max SPI speed is 5MHz. 4MHz will be safe.
SPI.setClockDivider(SPI_CLOCK_DIV4); //Set SPI bus speed
to 4MHz (16MHz / 4 = 4MHz)
MP3Clock = Mp3ReadRegister(SCI_CLOCKF);
Serial.print("SCI_ClockF = 0x");
Serial.println(MP3Clock, HEX);
//MP3 IC setup complete
}
void loop(){
//Let's play a track of a given number
sprintf(trackName, "track%03d.mp3", trackNumber); //Splice
the new file number into this file name
playMP3(trackName); //Go play trackXXX.mp3
//Once we are done playing or have exited the playback for
some reason, decide what track to play next
trackNumber++; //When we loop, advance to next track!
if(trackNumber > 100) {
Serial.println("Whoa there cowboy!"); //Soft limit. We
shouldn't be trying to open past track 100.
while(1);
}
}
//PlayMP3 pulls 32 byte chunks from the SD card and throws
them at the VS1053
//We monitor the DREQ (data request pin). If it goes low then
we determine if
//we need new data or not. If yes, pull new from SD card. Then
throw the data
//at the VS1053 until it is full.
void playMP3(char* fileName) {
if (!track.open(&root, fileName, O_READ)) { //Open the file in
read mode.
sprintf(errorMsg, "Failed to open %s", fileName);
Serial.println(errorMsg);
return;
}
Serial.println("Track open");
uint8_t mp3DataBuffer[32]; //Buffer of 32 bytes. VS1053 can
take 32 bytes at a go.
//track.read(mp3DataBuffer, sizeof(mp3DataBuffer)); //Read
the first 32 bytes of the song
int need_data = TRUE;
long replenish_time = millis();
Serial.println("Start MP3 decoding");
while(1) {
while(!digitalRead(MP3_DREQ)) {
//DREQ is low while the receive buffer is full
//You can do something else here, the buffer of the MP3 is
full and happy.
//Maybe set the volume or test to see how much we can
delay before we hear audible glitches
//If the MP3 IC is happy, but we need to read new data from
the SD, now is a great time to do so
if(need_data == TRUE) {
if(!track.read(mp3DataBuffer, sizeof(mp3DataBuffer))) {
//Try reading 32 new bytes of the song
//Oh no! There is no data left to read!
//Time to exit
break;
}
need_data = FALSE;
}
//Serial.println("."); //Print a character to show we are doing
nothing
//This is here to show how much time is spent transferring
new bytes to the VS1053 buffer. Relies on replenish_time
below.
Serial.print("Time to replenish buffer: ");
Serial.print(millis() - replenish_time, DEC);
Serial.print("ms");
//Test to see just how much we can do before the audio
starts to glitch
long start_time = millis();
//delay(150); //Do NOTHING - audible glitches
//delay(135); //Do NOTHING - audible glitches
//delay(120); //Do NOTHING - barely audible glitches
delay(100); //Do NOTHING - sounds fine
Serial.print(" Idle time: ");
Serial.print(millis() - start_time, DEC);
Serial.println("ms");
//Look at that! We can actually do quite a lot without the
audio glitching
//Now that we've completely emptied the VS1053 buffer
(2048 bytes) let's see how much
//time the VS1053 keeps the DREQ line high, indicating it
needs to be fed
replenish_time = millis();
}
if(need_data == TRUE){ //This is here in case we haven't
had any free time to load new data
if(!track.read(mp3DataBuffer, sizeof(mp3DataBuffer))) {
//Go out to SD card and try reading 32 new bytes of the song
//Oh no! There is no data left to read!
//Time to exit
break;
}
need_data = FALSE;
}
//Once DREQ is released (high) we now feed 32 bytes of
data to the VS1053 from our SD read buffer
digitalWrite(MP3_XDCS, LOW); //Select Data
for(int y = 0 ; y < sizeof(mp3DataBuffer) ; y++) {
SPI.transfer(mp3DataBuffer[y]); // Send SPI byte
}
digitalWrite(MP3_XDCS, HIGH); //Deselect Data
need_data = TRUE; //We've just dumped 32 bytes into
VS1053 so our SD read buffer is empty. Set flag so we go get
more data
}
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating transfer is complete
digitalWrite(MP3_XDCS, HIGH); //Deselect Data
track.close(); //Close out this track
sprintf(errorMsg, "Track %s done!", fileName);
Serial.println(errorMsg);
}
//Write to VS10xx register
//SCI: Data transfers are always 16bit. When a new SCI
operation comes in
//DREQ goes low. We then have to wait for DREQ to go high
again.
//XCS should be low for the full duration of operation.
void Mp3WriteRegister(unsigned char addressbyte, unsigned
char highbyte, unsigned char lowbyte){
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating IC is available
digitalWrite(MP3_XCS, LOW); //Select control
//SCI consists of instruction byte, address byte, and 16-bit
data word.
SPI.transfer(0x02); //Write instruction
SPI.transfer(addressbyte);
SPI.transfer(highbyte);
SPI.transfer(lowbyte);
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating command is complete
digitalWrite(MP3_XCS, HIGH); //Deselect Control
}
//Read the 16-bit value of a VS10xx register
unsigned int Mp3ReadRegister (unsigned char addressbyte){
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating IC is available
digitalWrite(MP3_XCS, LOW); //Select control
//SCI consists of instruction byte, address byte, and 16-bit
data word.
SPI.transfer(0x03); //Read instruction
SPI.transfer(addressbyte);
char response1 = SPI.transfer(0xFF); //Read the first byte
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating command is complete
char response2 = SPI.transfer(0xFF); //Read the second byte
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating command is complete
digitalWrite(MP3_XCS, HIGH); //Deselect Control
int resultvalue = response1 << 8;
resultvalue |= response2;
return resultvalue;
}
//Set VS10xx Volume Register
void Mp3SetVolume(unsigned char leftchannel, unsigned char
rightchannel){
Mp3WriteRegister(SCI_VOL, leftchannel, rightchannel);
}
Step 2:
Code to test the force
sensitive resistor - square
/*
AnalogReadSerial
Reads an analog input on pin 0, prints the result to the serial
monitor
This example code is in the public domain.
*/
void setup() {
Serial.begin(9600);
pinMode(13,OUTPUT);
}
void loop() {
Serial.println(analogRead(0), DEC);
if(analogRead(0) > 800)
{
Serial.println("LED on");
digitalWrite(13,255);
}
if(analogRead(0) < 800)
{
Serial.println("LED off");
digitalWrite(13,0);
}
}
Other:
Code to test the WiFly
Shield
/*
* WiFly Tutorial Example
* Copyright (c) 2010 SparkFun Electronics. All right reserved.
* Written by Chris Taylor
*
* This code was written to demonstrate the WiFly Tutorial from
SparkFun Electronics
*
* This code will associate with the SparkFun WiFi network,
serve a simple page, and
* "say" typed words using the Voice Box Shield
*
* http://www.sparkfun.com
*/
#include <string.h>
#include <SoftwareSerial.h>
#include <avr/pgmspace.h>
// SC16IS750 Register Definitions
#define THR 0x00 << 3
#define RHR 0x00 << 3
#define IER 0x01 << 3
#define FCR 0x02 << 3
#define IIR 0x02 << 3
#define LCR 0x03 << 3
#define MCR 0x04 << 3
#define LSR 0x05 << 3
#define MSR 0x06 << 3
#define SPR 0x07 << 3
#define TXFIFO 0x08 << 3
#define RXFIFO 0x09 << 3
#define DLAB 0x80 << 3
#define IODIR 0x0A << 3
#define IOSTATE 0x0B << 3
#define IOINTMSK 0x0C << 3
#define IOCTRL 0x0E << 3
#define EFCR 0x0F << 3
#define DLL 0x00 << 3
#define DLM 0x01 << 3
#define EFR 0x02 << 3
#define XON1 0x04 << 3
#define XON2 0x05 << 3
#define XOFF1 0x06 << 3
#define XOFF2 0x07 << 3
// SPI port definitions
#define CS 10
#define MOSI 11
#define MISO 12
#define SCK 13
// Voice Box Shield definitions
#define RES 3
#define SPK 4
#define txPin 2
SoftwareSerial speakjet = SoftwareSerial(0, txPin);
#define WORD_LIST_LENGTH 100
#define ASSOCIATE_TIMEOUT 5000
char is_get = 1;
// "Associated"
char message[] = {
132,187,187,137,8,189,8,128,154,191,129,176,0,255};
// SpeakJet numbers
char zero[] = {
167,7,128,7,149,164,0,255};
char one[] = {
147,14,135,8,141,0,255};
char two[] = {
8,191,162,31,0,255};
char three[] = {
8,190,148,8,128,0,255};
char four[] = {
186,7,137,153,0,0,255};
char five[] = {
186,157,166,0,255};
char six[] = {
8,187,129,14,194,7,187,0,255};
char seven[] = {
8,187,7,131,166,131,141,0,255};
char eight[] = {
154,4,191,0,255};
char nine[] = {
141,14,157,141,0,255};
char *numbers[] = {
zero, one, two, three, four, five, six, seven, eight, nine };
char ping[] = {
220,0,255};
char dot[] = {
175,8,135,191,0,255};
// WORD LIST
// Phoneme strings for SpeakJet
// Placed in program memory to save RAM
prog_char sj_the[] PROGMEM = {
8,169,8,128,0,0,255};
prog_char sj_of[] PROGMEM = {
8,134,166,0,0,255};
prog_char sj_and1[] PROGMEM = {
8,132,8,141,177,0,0,255};
prog_char sj_a1[] PROGMEM = {
154,128,0,0,255};
prog_char sj_to[] PROGMEM = {
8,191,162,0,0,255};
prog_char sj_in[] PROGMEM = {
8,129,8,141,0,0,255};
prog_char sj_is[] PROGMEM = {
8,129,167,0,0,255};
prog_char sj_you[] PROGMEM = {
8,160,0,0,255};
prog_char sj_that[] PROGMEM = {
169,8,132,8,191,0,0,255};
prog_char sj_it[] PROGMEM = {
8,129,8,191,0,0,255};
prog_char sj_he[] PROGMEM = {
183,128,0,0,255};
prog_char sj_for1[] PROGMEM = {
186,153,0,0,255};
prog_char sj_was[] PROGMEM = {
147,134,167,0,0,255};
prog_char sj_on[] PROGMEM = {
135,135,142,0,0,255};
prog_char sj_are[] PROGMEM = {
152,0,0,255};
prog_char sj_as[] PROGMEM = {
132,8,167,0,0,255};
prog_char sj_with[] PROGMEM = {
147,129,8,8,190,0,0,255};
prog_char sj_his[] PROGMEM = {
183,129,167,0,0,255};
prog_char sj_they[] PROGMEM = {
8,169,154,0,0,255};
prog_char sj_at[] PROGMEM = {
132,8,191,0,0,255};
prog_char sj_be[] PROGMEM = {
170,128,128,0,0,255};
prog_char sj_this1[] PROGMEM = {
8,169,8,129,187,0,0,255};
prog_char sj_from[] PROGMEM = {
186,148,134,8,140,0,0,255};
prog_char sj_i1[] PROGMEM = {
157,0,0,255};
prog_char sj_have[] PROGMEM = {
183,8,132,166,0,0,255};
prog_char sj_or1[] PROGMEM = {
153,0,0,255};
prog_char sj_by[] PROGMEM = {
171,157,0,0,255};
prog_char sj_one[] PROGMEM = {
147,14,135,8,141,0,0,255};
prog_char sj_not1[] PROGMEM = {
141,135,191,0,0,255};
prog_char sj_what[] PROGMEM = {
185,8,135,191,0,0,255};
prog_char sj_all[] PROGMEM = {
8,136,8,146,0,0,255};
prog_char sj_when[] PROGMEM = {
185,8,131,8,141,0,0,255};
prog_char sj_we[] PROGMEM = {
147,8,128,0,0,255};
prog_char sj_there[] PROGMEM = {
8,169,150,0,0,255};
prog_char sj_can[] PROGMEM = {
194,8,132,141,0,0,255};
prog_char sj_an[] PROGMEM = {
132,132,141,0,0,255};
prog_char sj_your[] PROGMEM = {
128,153,0,0,255};
prog_char sj_which[] PROGMEM = {
185,129,8,182,0,0,255};
prog_char sj_their[] PROGMEM = {
8,169,150,0,0,255};
prog_char sj_if1[] PROGMEM = {
129,186,186,0,0,255};
prog_char sj_do1[] PROGMEM = {
174,162,0,0,255};
prog_char sj_will[] PROGMEM = {
147,129,8,145,0,0,255};
prog_char sj_each[] PROGMEM = {
128,128,4,182,0,0,255};
prog_char sj_about[] PROGMEM = {
134,173,163,191,0,0,255};
prog_char sj_how[] PROGMEM = {
184,8,163,0,0,255};
prog_char sj_up[] PROGMEM = {
8,134,199,0,0,255};
prog_char sj_out[] PROGMEM = {
163,191,0,0,255};
prog_char sj_them[] PROGMEM = {
8,169,131,8,140,0,0,255};
prog_char sj_then[] PROGMEM = {
8,169,131,8,142,0,0,255};
prog_char sj_she[] PROGMEM = {
8,189,8,128,0,0,255};
prog_char sj_many[] PROGMEM = {
140,131,141,128,0,0,255};
prog_char sj_some[] PROGMEM = {
8,187,134,140,0,0,255};
prog_char sj_so[] PROGMEM = {
8,188,7,164,7,147,0,0,255};
prog_char sj_these[] PROGMEM = {
8,169,8,128,7,167,0,0,255};
prog_char sj_would[] PROGMEM = {
147,8,138,177,0,0,255};
prog_char sj_other[] PROGMEM = {
134,190,151,0,0,255};
prog_char sj_into[] PROGMEM = {
129,141,191,162,0,0,255};
prog_char sj_more[] PROGMEM = {
140,7,137,153,0,0,255};
prog_char sj_her[] PROGMEM = {
183,151,0,0,255};
prog_char sj_two[] PROGMEM = {
8,191,162,0,0,255};
prog_char sj_like[] PROGMEM = {
145,7,135,7,155,196,0,0,255};
prog_char sj_him[] PROGMEM = {
183,8,129,140,0,0,255};
prog_char sj_see[] PROGMEM = {
187,187,128,128,0,0,255};
prog_char sj_time[] PROGMEM = {
8,191,157,8,140,0,0,255};
prog_char sj_could[] PROGMEM = {
195,138,138,177,0,0,255};
prog_char sj_no[] PROGMEM = {
142,164,0,0,255};
prog_char sj_make[] PROGMEM = {
140,154,196,0,0,255};
prog_char sj_than[] PROGMEM = {
169,8,132,8,142,0,0,255};
prog_char sj_first[] PROGMEM = {
186,151,187,191,0,0,255};
prog_char sj_now[] PROGMEM = {
142,163,0,0,255};
prog_char sj_people[] PROGMEM = {
198,7,128,198,8,145,0,0,255};
prog_char sj_my[] PROGMEM = {
140,155,0,0,255};
prog_char sj_over[] PROGMEM = {
8,137,7,166,151,0,0,255};
prog_char sj_down[] PROGMEM = {
175,163,141,0,0,255};
prog_char sj_only[] PROGMEM = {
137,142,145,128,0,0,255};
prog_char sj_find[] PROGMEM = {
186,155,141,177,0,0,255};
prog_char sj_use[] PROGMEM = {
160,167,0,0,255};
prog_char sj_may[] PROGMEM = {
140,154,0,0,255};
prog_char sj_water[] PROGMEM = {
147,135,191,7,151,0,0,255};
prog_char sj_long1[] PROGMEM = {
146,135,8,144,0,0,255};
prog_char sj_little[] PROGMEM = {
145,129,191,131,145,0,0,255};
prog_char sj_feed[] PROGMEM = {
186,128,128,176,0,0,255};
prog_char sj_very[] PROGMEM = {
166,150,7,128,0,0,255};
prog_char sj_after[] PROGMEM = {
132,186,191,7,151,0,0,255};
prog_char sj_me[] PROGMEM = {
140,128,128,0,0,255};
prog_char sj_words[] PROGMEM = {
147,151,176,7,167,0,0,255};
prog_char sj_just[] PROGMEM = {
165,133,187,191,0,0,255};
prog_char sj_cat[] PROGMEM = {
194,132,191,0,0,255};
prog_char sj_where[] PROGMEM = {
147,150,0,0,255};
prog_char sj_most[] PROGMEM = {
140,8,137,187,191,0,0,255};
prog_char sj_know[] PROGMEM = {
142,137,164,0,0,255};
prog_char sj_dead[] PROGMEM = {
174,131,174,0,0,255};
prog_char sj_get[] PROGMEM = {
8,178,8,131,191,0,0,255};
prog_char sj_through[] PROGMEM = {
8,190,148,8,139,0,0,255};
prog_char sj_back[] PROGMEM = {
170,8,132,4,196,0,0,255};
prog_char sj_much[] PROGMEM = {
140,134,182,0,0,255};
prog_char sj_go[] PROGMEM = {
8,179,8,164,0,0,255};
prog_char sj_good[] PROGMEM = {
8,179,138,138,177,0,0,255};
prog_char sj_new1[] PROGMEM = {
141,160,0,0,255};
prog_char sj_write[] PROGMEM = {
148,155,191,0,0,255};
PROGMEM const char *SpeakJetDictionary[] = {
sj_the,sj_of,sj_and1,sj_a1,sj_to,sj_in,sj_is,sj_you,sj_that,sj_it,sj
_he,sj_for1,sj_was,sj_on,sj_are,sj_as,
sj_with,sj_his,sj_they,sj_at,sj_be,sj_this1,sj_from,sj_i1,sj_have,
sj_or1,sj_by,sj_one,sj_not1,sj_what,sj_all,
sj_when,sj_we,sj_there,sj_can,sj_an,sj_your,sj_which,sj_their,s
j_if1,sj_do1,sj_will,sj_each,sj_about,sj_how,
sj_up,sj_out,sj_them,sj_then,sj_she,sj_many,sj_some,sj_so,sj_
these,sj_would,sj_other,sj_into,sj_more,sj_her,
sj_two,sj_like,sj_him,sj_see,sj_time,sj_could,sj_no,sj_make,sj_t
han,sj_first,sj_now,sj_people,sj_my,sj_over,
sj_down,sj_only,sj_find,sj_use,sj_may,sj_water,sj_long1,sj_little
,sj_feed,sj_very,sj_after,sj_me,sj_words,
sj_just,sj_cat,sj_where,sj_most,sj_know,sj_dead,sj_get,sj_throu
gh,sj_back,sj_much,sj_go,sj_good,sj_new1,sj_write
};
// SpeakJet lookup table
// Local searchable table to find indices for phoneme strings
char *SpeakJetLookup[] = {
"the","of","and","a","to","in","is","you","that","it","he","for","was","
on","are","as","with","his","they","at","be","this",
"from","i","have","or","by","one","not","what","all","when","we","t
here","can","an","your","which","their","if","do","will",
"each","about","how","up","out","them","then","she","many","so
me","so","these","would","other","into","more","her","two","like",
"him","see","time","could","no","make","than","first","now","peopl
e","my","over","down","only","find","use","may","water","long",
"little","feed","very","after","me","words","just","cat","where","mo
st","know","dead","get","through","back","much","go","good",
"new","write"
};
// Flags
char incoming_data;
char SPI_init_flag = 0;
char polling = 0;
char TX_Fifo_Address = THR;
int i = 0;
int j = 0;
int k = 0;
char clr = 0;
struct SPI_UART_cfg
// Defines register values for SC16IS750
{
char DivL,DivM,DataFormat,Flow;
};
struct SPI_UART_cfg SPI_Uart_config = {
0x60,0x00,0x03,0x10};
char auth_level[] = "3";
char auth_phrase[] = "56925100";
char port_listen[] = "80";
char ssid[] = "ChocoMocha";
char ip_address[30]; // Stores IP address for SpeakJet
void setup()
{
// SPI Init
pinMode(MOSI, OUTPUT);
pinMode(MISO, INPUT);
pinMode(SCK,OUTPUT);
pinMode(CS,OUTPUT);
digitalWrite(CS,HIGH); //disable device
// SpeakJet Init
pinMode(txPin, OUTPUT);
pinMode(SPK, INPUT);
pinMode(RES, OUTPUT);
// SPI Start
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
clr=SPSR;
clr=SPDR;
delay(10);
Serial.begin(9600);
speakjet.begin(9600);
// Reset the SpeakJet
digitalWrite(RES, LOW);
delay(100);
digitalWrite(RES, HIGH);
Serial.println("\n\r\n\rWiFly Shield Terminal Routine");
// Initialize and test the SPI-Uart bridge
if(SPI_Uart_Init()){
Serial.println("Bridge initialized successfully!");
}
else{
Serial.println("Could not initialize bridge, locking up.\n\r");
while(1);
}
// Autoconnect to local WiFi using parameters from the global
vars
autoconnect();
}
void loop()
{
// Exit command mode if we haven't already
SPI_Uart_println("exit");
delay(500);
Flush_RX();
while(1)
{
while(!Have_Client()); // Wait on client
Serial.println("\n\rConnection opened.");
// Send HTML
HTML_print("<html>");
HTML_print("<title>SparkFun SpeakJet Server</title>");
HTML_print("<h1><center>");
HTML_print("<img
src=\"http://static.sparkfun.com/images/framework/logo.gif\">");
HTML_print("</center></h1>");
HTML_print("Type some shizz up:");
HTML_print("<form name=\"input\" action=\"\"
method=\"post\">");
HTML_print("<input type=\"text\" name=\"%\" size=\"100\">");
HTML_print("<input type=\"submit\" value=\"Submit\">");
HTML_print("</form>");
// Send word list
HTML_print("<h1>Word List:</h1><p>");
for(int wordn = 0; wordn < WORD_LIST_LENGTH; wordn++)
{
HTML_print(SpeakJetLookup[wordn]);
HTML_print(" ");
}
HTML_print("</html>");
delay(500);
// Enter command mode and close connection
SPI_Uart_print("$$$");
delay(500);
SPI_Uart_println("close");
delay(1000);
SPI_Uart_println("exit");
delay(500);
Serial.println("Connection closed.\n\r");
Flush_RX();
}
}
void select(void) // Select SC16IS750
{
digitalWrite(CS,LOW);
}
void deselect(void) // Deselect SC16IS750
{
digitalWrite(CS,HIGH);
}
char Have_Client(void)
{
if(SPI_Uart_ReadByte(LSR) & 0x01){ // Wait for characters
from a connection
Serial.println("Client request...");
is_get = 1;
Parse_Request(); // Check if request is a POST
Flush_RX();
return 1;
}
else{
return 0;
}
}
char SPI_Uart_Init(void)
// Initialize SC16IS750
{
char data = 0;
SPI_Uart_WriteByte(LCR,0x80); // 0x80 to program baudrate
SPI_Uart_WriteByte(DLL,SPI_Uart_config.DivL); //0x50 =
9600 with Xtal = 12.288MHz
SPI_Uart_WriteByte(DLM,SPI_Uart_config.DivM);
SPI_Uart_WriteByte(LCR, 0xBF); // access EFR register
SPI_Uart_WriteByte(EFR, SPI_Uart_config.Flow); // enable
enhanced registers
SPI_Uart_WriteByte(LCR, SPI_Uart_config.DataFormat); // 8
data bit, 1 stop bit, no parity
SPI_Uart_WriteByte(FCR, 0x06); // reset TXFIFO, reset
RXFIFO, non FIFO mode
SPI_Uart_WriteByte(FCR, 0x01); // enable FIFO mode
// Perform read/write test to check if UART is working
SPI_Uart_WriteByte(SPR,'H');
data = SPI_Uart_ReadByte(SPR);
if(data == 'H'){
return 1;
}
else{
return 0;
}
}
void SPI_Uart_WriteByte(char address, char data)
// Write single byte to register at <address>
{
long int length;
char senddata[2];
senddata[0] = address;
senddata[1] = data;
select();
length = SPI_Write(senddata, 2);
deselect();
}
long int SPI_Write(char* srcptr, long int length)
// Write string to SC16IS750
{
for(long int i = 0; i < length; i++)
{
spi_transfer(srcptr[i]);
}
return length;
}
void SPI_Uart_WriteArray(char *data, long int NumBytes)
// Write string to THR of SC16IS750
{
long int length;
select();
length = SPI_Write(&TX_Fifo_Address,1);
while(NumBytes > 16)
{
length = SPI_Write(data,16);
NumBytes -= 16;
data += 16;
}
length = SPI_Write(data,NumBytes);
deselect();
}
char SPI_Uart_ReadByte(char address)
// Read SC16IS750 register at <address>
{
char data;
address = (address | 0x80);
select();
spi_transfer(address);
data = spi_transfer(0xFF);
deselect();
return data;
}
char autoconnect(void)
// Automatically connect to WiFi network using parameters in
global variables
{
// Exit command mode if we haven't already
SPI_Uart_println("");
SPI_Uart_println("exit");
delay(500);
// Enter command mode
SPI_Uart_print("$$$");
delay(500);
// Reboot to get device into known state
Serial.println("Rebooting");
SPI_Uart_println("reboot");
delay(3000);
// Enter command mode
Serial.println("Entering command mode.");
SPI_Uart_print("$$$");
delay(500);
// Set the security authorization level of the WiFly using
<auth_level>
SPI_Uart_print("set w a ");
SPI_Uart_println(auth_level);
delay(500);
Serial.print("Set wlan to authorization level ");
Serial.println(auth_level);
// Set the WiFly authorization phrase using <auth_phrase>
SPI_Uart_print("set w p ");
SPI_Uart_println(auth_phrase);
delay(500);
Serial.print("Set security phrase to ");
Serial.println(auth_phrase);
// Set the WiFly localport using <port_listen>
SPI_Uart_print("set i l ");
SPI_Uart_println(port_listen);
delay(500);
Serial.print("Set IP localport to ");
Serial.println(port_listen);
// Turn off WiFly UART connection indicators
SPI_Uart_println("set comm remote 0");
delay(500);
SPI_Uart_println("set comm open 0");
delay(500);
SPI_Uart_println("set comm close 0");
delay(500);
// Join WiFi network <ssid>
Serial.print("Joining '");
Serial.print(ssid);
Serial.println("'");
delay(100);
Flush_RX();
delay(100);
SPI_Uart_print("join ");
SPI_Uart_println(ssid);
for(int p = 0; p < 5; p++) // Give WiFly time to associate
{
Serial.print(".");
speakjet.print(ping);
delay(ASSOCIATE_TIMEOUT/5);
}
Serial.println("");
// Flush characters from SC16IS750
Flush_RX();
// Check for association
SPI_Uart_println("show c");
if(Wait_On_Response_Char(13) != '0')
// Failed, try again
{
Serial.print("Failed to associate with '");
Serial.print(ssid);
Serial.println("'\n\rRetrying...");
Flush_RX();
autoconnect();
}
else
// Success, SpeakJet says "Associated" and IP address
{
Serial.println("Associated!");
speakjet.print(message);
delay(1000);
SPI_Uart_println("get ip");
get_ip();
}
}
void Flush_RX(void)
// Flush characters from the SC16IS750 so we only see what
we want to see on the terminal
{
int j = 0;
while(j < 4000)
{
if((SPI_Uart_ReadByte(LSR) & 0x01))
{
incoming_data = SPI_Uart_ReadByte(RHR);
}
else
{
j++;
}
}
}
void Parse_Request(void)
// Parse POST data into separate words, look up, and "say"
words using SpeakJet
{
int j = 0, i = 0, k = 0;
char a = 0;
char printflag = 0;
char message[128];
char poem_word[16] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
char buffer[16] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
char sentinel = 0;
while(j < 4000) // Read in full message
{
if((SPI_Uart_ReadByte(LSR) & 0x01))
{
incoming_data = SPI_Uart_ReadByte(RHR);
if(printflag){
message[i-3] = incoming_data;
i++;
}
else if(incoming_data == '%'){
printflag = 1;
}
}
else
{
j++;
}
}
i -= 3;
j = 0;
while(k<i) // Split <message> into words and say
{
while(message[k] != '+' && k < i)
{
poem_word[j] = message[k];
k++;
j++;
}
k++;
for(int index = 0; index < WORD_LIST_LENGTH; index++)
{
if(strcmp(poem_word,SpeakJetLookup[index]) == 0) // Look
up word in SpeakJetLookup
{
// Serial.print(index,DEC); // Print index of word for
debugging
strcpy_P(buffer,
(char*)pgm_read_word(&(SpeakJetDictionary[index]))); // Copy
word from program memory using lookup index
speakjet.print(buffer); // Send string to the VoiceBox
is_get = 0; // Flag as POST request
delay(600); // Wait in between words
}
}
j = 0;
for(char c = 0; c < 16; c++){ // Clear buffers
poem_word[c] = 0;
buffer[c] = 0;
}
}
}
char Wait_On_Response_Char(char num)
// Check for character number <num> from a response string
{
i = 1;
while(1)
{
if((SPI_Uart_ReadByte(LSR) & 0x01))
{
incoming_data = SPI_Uart_ReadByte(RHR);
//Serial.print(incoming_data, BYTE); // Print data for
debugging
if(i == num){
return incoming_data;
}
else{
i++;
}
}
}
}
void SPI_Uart_println(char *data)
// Write <data> to THR of SC16IS750 followed by a carriage
return
{
SPI_Uart_WriteArray(data,strlen(data));
SPI_Uart_WriteByte(THR, 0x0d);
}
void HTML_print(char *data)
// Write <data> to THR of SC16IS750 followed by a delay
{
SPI_Uart_WriteArray(data,strlen(data));
delay(30);
}
void SPI_Uart_print(char *data)
// Obfuscation of SPI_Uart_WriteArray that uses strlen instead
of hardcoded length
{
SPI_Uart_WriteArray(data,strlen(data));
}
char spi_transfer(volatile char data)
{
SPDR = data; // Start the transmission
while (!(SPSR & (1<<SPIF))) // Wait for the end of the
transmission
{
};
return SPDR; // return the received byte
}
void get_ip(void)
// Parse out IP address of WiFly and "say" it with the speakjet
{
char returns = 0;
char ip_index = 0;
char a = 0;
// Parse
while(returns < 6)
{
if((SPI_Uart_ReadByte(LSR) & 0x01))
{
incoming_data = SPI_Uart_ReadByte(RHR);
if(incoming_data == 0x0d)
{
returns++;
}
if(returns > 4)
{
//Serial.print(incoming_data,BYTE);
ip_address[ip_index] = incoming_data;
ip_index++;
}
}
}
delay(1000);
// Say IP address
while(ip_address[a] != ':')
{
if(a < 5){
}
else if(ip_address[a] == '.'){
Serial.print(ip_address[a]);
speakjet.print(dot);
delay(500);
}
else{
Serial.print(ip_address[a]);
speakjet.print(numbers[(ip_address[a] - 48)]);
delay(500);
}
a++;
}
Serial.println("");
Flush_RX(); // Do not print characters
}
Step 3:
Code to test the basic 16x2
character LCD
/*
LiquidCrystal Library - Hello World
Demonstrates the use a 16x2 LCD display. The LiquidCrystal
library works with all LCD displays that are compatible with the
Hitachi HD44780 driver. There are many of them out there,
and you
can usually tell them by the 16-pin interface.
This sketch prints "Hello World!" to the LCD
and shows the time.
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
Library originally added 18 Apr 2008
by David A. Mellis
library modified 5 Jul 2009
by Limor Fried (http://www.ladyada.net)
example added 9 Jul 2009
by Tom Igoe
modified 22 Nov 2010
by Tom Igoe
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/LiquidCrystal
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print("hello, world!");
}
void loop() {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with
0):
//lcd.setCursor(0, 1);
// print the number of seconds since reset:
//lcd.print(millis()/1000);
}
Last step:
Code that combined all
code
/*
4-28-2011
Spark Fun Electronics 2011
Nathan Seidle
This code is public domain but you buy me a beer if you use
this and we meet someday (Beerware license).
This example code plays a MP3 from the SD card called
'track001.mp3'. The theory is that you can load a
microSD card up with a bunch of MP3s and then play a given
'track' depending on some sort of input such
as which pin is pulled low.
It relies on the sdfatlib from Bill Greiman:
http://code.google.com/p/sdfatlib/
You will need to download and install his library. To compile,
you MUST change Sd2PinMap.h of the SDfatlib!
The default SS_PIN = 10;. You must change this line under the
ATmega328/Arduino area of code to
uint8_t const SS_PIN = 9;. This will cause the sdfatlib to use
pin 9 as the 'chip select' for the
microSD card on pin 9 of the Arduino so that the layout of the
shield works.
Attach the shield to an Arduino. Load code (after editing
Sd2PinMap.h) then open the terminal at 57600bps. This
example shows that it takes ~30ms to load up the VS1053
buffer. We can then do whatever we want for ~100ms
before we need to return to filling the buffer (for another 30ms).
This code is heavily based on the example code I wrote to
control the MP3 shield found here:
http://www.sparkfun.com/products/9736
This example code extends the previous example by reading
the MP3 from an SD card and file rather than from internal
memory of the ATmega. Because the current MP3 shield does
not have a microSD socket, you will need to add the microSD
shield to your Arduino stack.
The main gotcha from all of this is that you have to make sure
your CS pins for each device on an SPI bus is carefully
declared. For the SS pin (aka CS) on the SD FAT libaray, you
need to correctly set it within Sd2PinMap.h. The default
pin in Sd2PinMap.h is 10. If you're using the SparkFun
microSD shield with the SparkFun MP3 shield, the SD CS pin
is pin 9.
Four pins are needed to control the VS1503:
DREQ
CS
DCS
Reset (optional but good to have access to)
Plus the SPI bus
Only the SPI bus pins and another CS pin are needed to
control the microSD card.
What surprised me is the fact that with a normal MP3 we can
do other things for up to 100ms while the MP3 IC crunches
through it's fairly large buffer of 2048 bytes. As long as you
keep your sensor checks or serial reporting to under
100ms and leave ~30ms to then replenish the MP3 buffer, you
can do quite a lot while the MP3 is playing glitch free.
*/
// include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
//LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
LiquidCrystal lcd(5, 4, 13, 3, 11, 12);
#include <SPI.h>
//Add the SdFat Libraries
#include <SdFat.h>
#include <SdFatUtil.h>
//Create the variables to be used by SdFat Library
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile track;
//This is the name of the file on the microSD card you would like
to play
//Stick with normal 8.3 nomeclature. All lower-case works well.
//Note: you must name the tracks on the SD card with 001, 002,
003, etc.
//For example, the code is expecting to play 'track002.mp3', not
track2.mp3.
char trackName[] = "track001.mp3";
int trackNumber = 1;
char errorMsg[100]; //This is a generic array used for sprintf of
error messages
#define TRUE 0
#define FALSE 1
//MP3 Player Shield pin mapping. See the schematic
#define MP3_XCS 6 //Control Chip Select Pin (for accessing
SPI Control/Status registers)
#define MP3_XDCS 7 //Data Chip Select / BSYNC Pin
#define MP3_DREQ 2 //Data Request Pin: Player asks for
more data
#define MP3_RESET 8 //Reset is active low
//Remember you have to edit the Sd2PinMap.h of the sdfatlib
library to correct control the SD card.
//VS10xx SCI Registers
#define SCI_MODE 0x00
#define SCI_STATUS 0x01
#define SCI_BASS 0x02
#define SCI_CLOCKF 0x03
#define SCI_DECODE_TIME 0x04
#define SCI_AUDATA 0x05
#define SCI_WRAM 0x06
#define SCI_WRAMADDR 0x07
#define SCI_HDAT0 0x08
#define SCI_HDAT1 0x09
#define SCI_AIADDR 0x0A
#define SCI_VOL 0x0B
#define SCI_AICTRL0 0x0C
#define SCI_AICTRL1 0x0D
#define SCI_AICTRL2 0x0E
#define SCI_AICTRL3 0x0F
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 1);
// Print a message to the LCD.
lcd.print("mushion");
pinMode(MP3_DREQ, INPUT);
pinMode(MP3_XCS, OUTPUT);
pinMode(MP3_XDCS, OUTPUT);
pinMode(MP3_RESET, OUTPUT);
digitalWrite(MP3_XCS, HIGH); //Deselect Control
digitalWrite(MP3_XDCS, HIGH); //Deselect Data
digitalWrite(MP3_RESET, LOW); //Put VS1053 into hardware
reset
Serial.begin(57600); //Use serial for debugging
Serial.println("MP3 Testing");
//Setup SD card interface
pinMode(10, OUTPUT); //Pin 10 must be set as an output
for the SD communication to work.
if (!card.init(SPI_FULL_SPEED)) Serial.println("Error: Card
init"); //Initialize the SD card and configure the I/O pins.
if (!volume.init(&card)) Serial.println("Error: Volume ini");
//Initialize a volume on the SD card.
if (!root.openRoot(&volume)) Serial.println("Error: Opening
root"); //Open the root directory in the volume.
//We have no need to setup SPI for VS1053 because this has
already been done by the SDfatlib
//From page 12 of datasheet, max SCI reads are CLKI/7. Input
clock is 12.288MHz.
//Internal clock multiplier is 1.0x after power up.
//Therefore, max SPI speed is 1.75MHz. We will use 1MHz to
be safe.
SPI.setClockDivider(SPI_CLOCK_DIV16); //Set SPI bus
speed to 1MHz (16MHz / 16 = 1MHz)
SPI.transfer(0xFF); //Throw a dummy byte at the bus
//Initialize VS1053 chip
delay(10);
digitalWrite(MP3_RESET, HIGH); //Bring up VS1053
//delay(10); //We don't need this delay because any register
changes will check for a high DREQ
//Mp3SetVolume(20, 20); //Set initial volume (20 = -10dB)
LOUD
Mp3SetVolume(0, 0); //Set initial volume (20 = -10dB)
Manageable
//Mp3SetVolume(80, 80); //Set initial volume (20 = -10dB)
More quiet
//Let's check the status of the VS1053
int MP3Mode = Mp3ReadRegister(SCI_MODE);
int MP3Status = Mp3ReadRegister(SCI_STATUS);
int MP3Clock = Mp3ReadRegister(SCI_CLOCKF);
Serial.print("SCI_Mode (0x4800) = 0x");
Serial.println(MP3Mode, HEX);
Serial.print("SCI_Status (0x48) = 0x");
Serial.println(MP3Status, HEX);
int vsVersion = (MP3Status >> 4) & 0x000F; //Mask out only
the four version bits
Serial.print("VS Version (VS1053 is 4) = ");
Serial.println(vsVersion, DEC); //The 1053B should respond
with 4. VS1001 = 0, VS1011 = 1, VS1002 = 2, VS1003 = 3
Serial.print("SCI_ClockF = 0x");
Serial.println(MP3Clock, HEX);
//Now that we have the VS1053 up and running, increase the
internal clock multiplier and up our SPI rate
Mp3WriteRegister(SCI_CLOCKF, 0x60, 0x00); //Set multiplier
to 3.0x
//From page 12 of datasheet, max SCI reads are CLKI/7. Input
clock is 12.288MHz.
//Internal clock multiplier is now 3x.
//Therefore, max SPI speed is 5MHz. 4MHz will be safe.
SPI.setClockDivider(SPI_CLOCK_DIV4); //Set SPI bus speed
to 4MHz (16MHz / 4 = 4MHz)
MP3Clock = Mp3ReadRegister(SCI_CLOCKF);
Serial.print("SCI_ClockF = 0x");
Serial.println(MP3Clock, HEX);
//MP3 IC setup complete
}
void loop(){
/*lcd.setCursor(0, 1);
// print the number of seconds since reset:
lcd.print(millis()/1000);*/
Serial.println (analogRead (0), DEC);
if (analogRead (0) > 800)
{
Serial.println ("LED on");
digitalWrite (13, 255);
}
if (analogRead (0) < 800)
{
Serial.println ("LED off");
digitalWrite (13, 0);
}
if(analogRead(0) > 800)
{
Serial.println("pushed!!!!!! HEARTBEATS ON");
//Let's play a track of a given number
sprintf(trackName, "track%03d.mp3", trackNumber); //Splice
the new file number into this file name
playMP3(trackName); //Go play trackXXX.mp3
/*Serial.println ("LED on");
digitalWrite (13, 255);*/
//Once we are done playing or have exited the playback for
some reason, decide what track to play next
//trackNumber++; //When we loop, advance to next track!
}
/*if(trackNumber > 5) {
Serial.println("Whoa there cowboy!"); //Soft limit. We
shouldn't be trying to open past track 100.
while(1);
}*/
}
//PlayMP3 pulls 32 byte chunks from the SD card and throws
them at the VS1053
//We monitor the DREQ (data request pin). If it goes low then
we determine if
//we need new data or not. If yes, pull new from SD card. Then
throw the data
//at the VS1053 until it is full.
void playMP3(char* fileName) {
if (!track.open(&root, fileName, O_READ)) { //Open the file in
read mode.
sprintf(errorMsg, "Failed to open %s", fileName);
Serial.println(errorMsg);
return;
}
Serial.println("Track open");
uint8_t mp3DataBuffer[32]; //Buffer of 32 bytes. VS1053 can
take 32 bytes at a go.
//track.read(mp3DataBuffer, sizeof(mp3DataBuffer)); //Read
the first 32 bytes of the song
int need_data = TRUE;
long replenish_time = millis();
Serial.println("Start MP3 decoding");
while(1) {
while(!digitalRead(MP3_DREQ)) {
//DREQ is low while the receive buffer is full
//You can do something else here, the buffer of the MP3 is
full and happy.
//Maybe set the volume or test to see how much we can
delay before we hear audible glitches
//If the MP3 IC is happy, but we need to read new data from
the SD, now is a great time to do so
if(need_data == TRUE) {
if(!track.read(mp3DataBuffer, sizeof(mp3DataBuffer))) {
//Try reading 32 new bytes of the song
//Oh no! There is no data left to read!
//Time to exit
break;
}
need_data = FALSE;
}
//Serial.println("."); //Print a character to show we are doing
nothing
//This is here to show how much time is spent transferring
new bytes to the VS1053 buffer. Relies on replenish_time
below.
Serial.print("Time to replenish buffer: ");
Serial.print(millis() - replenish_time, DEC);
Serial.print("ms");
//Test to see just how much we can do before the audio
starts to glitch
long start_time = millis();
//delay(150); //Do NOTHING - audible glitches
//delay(135); //Do NOTHING - audible glitches
//delay(120); //Do NOTHING - barely audible glitches
delay(100); //Do NOTHING - sounds fine
Serial.print(" Idle time: ");
Serial.print(millis() - start_time, DEC);
Serial.println("ms");
//Look at that! We can actually do quite a lot without the
audio glitching
//Now that we've completely emptied the VS1053 buffer
(2048 bytes) let's see how much
//time the VS1053 keeps the DREQ line high, indicating it
needs to be fed
replenish_time = millis();
}
if(need_data == TRUE){ //This is here in case we haven't
had any free time to load new data
if(!track.read(mp3DataBuffer, sizeof(mp3DataBuffer))) {
//Go out to SD card and try reading 32 new bytes of the song
//Oh no! There is no data left to read!
//Time to exit
break;
}
need_data = FALSE;
}
//Once DREQ is released (high) we now feed 32 bytes of
data to the VS1053 from our SD read buffer
digitalWrite(MP3_XDCS, LOW); //Select Data
for(int y = 0 ; y < sizeof(mp3DataBuffer) ; y++) {
SPI.transfer(mp3DataBuffer[y]); // Send SPI byte
}
digitalWrite(MP3_XDCS, HIGH); //Deselect Data
need_data = TRUE; //We've just dumped 32 bytes into
VS1053 so our SD read buffer is empty. Set flag so we go get
more data
}
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating transfer is complete
digitalWrite(MP3_XDCS, HIGH); //Deselect Data
track.close(); //Close out this track
sprintf(errorMsg, "Track %s done!", fileName);
Serial.println(errorMsg);
}
//Write to VS10xx register
//SCI: Data transfers are always 16bit. When a new SCI
operation comes in
//DREQ goes low. We then have to wait for DREQ to go high
again.
//XCS should be low for the full duration of operation.
void Mp3WriteRegister(unsigned char addressbyte, unsigned
char highbyte, unsigned char lowbyte){
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating IC is available
digitalWrite(MP3_XCS, LOW); //Select control
//SCI consists of instruction byte, address byte, and 16-bit
data word.
SPI.transfer(0x02); //Write instruction
SPI.transfer(addressbyte);
SPI.transfer(highbyte);
SPI.transfer(lowbyte);
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating command is complete
digitalWrite(MP3_XCS, HIGH); //Deselect Control
}
//Read the 16-bit value of a VS10xx register
unsigned int Mp3ReadRegister (unsigned char addressbyte){
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating IC is available
digitalWrite(MP3_XCS, LOW); //Select control
//SCI consists of instruction byte, address byte, and 16-bit
data word.
SPI.transfer(0x03); //Read instruction
SPI.transfer(addressbyte);
char response1 = SPI.transfer(0xFF); //Read the first byte
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating command is complete
char response2 = SPI.transfer(0xFF); //Read the second byte
while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high
indicating command is complete
digitalWrite(MP3_XCS, HIGH); //Deselect Control
int resultvalue = response1 << 8;
resultvalue |= response2;
return resultvalue;
}
//Set VS10xx Volume Register
void Mp3SetVolume(unsigned char leftchannel, unsigned char
rightchannel){
Mp3WriteRegister(SCI_VOL, leftchannel, rightchannel);
}