My initial thought on this project was to make something fun my team could use when they were having a hard day, or just needed to lighten the mood. As I was looking around my office, I noticed and old Magic 8ball.
I figured I could do something similar and greatly expand the number of answers that were possible. Make it where you could provide your own answers and a large number of them.
I also noticed a Google voice kit that I had picked up for next to nothing. I haven't done anything with that kit and this seemed like the perfect opportunity.
Hooking everything upConnecting everything up is pretty straight forward.
- Determine the LED and switch connections on the arcade button.
- Solder some jumper wires to the connection.
- Plug the cathode(negative) lead on the LED and one side of the switch the ground(GND) pins in the expansion board.
- Plug the other side of the switch into D2 on the expansion board.
- Plug the anode(positive) lead on the LED into D13 on the expansion board.
- Solder a speaker wires to and 1/8 audio plug. Make the black the outside(ground), and the red the very middle connection. Shouldn't really matter as all the audio output is set to MONO for this project.
- Follow the directions on assembling the Google enclosure.
- Mount the Spresense in the bottom of the enclosure. I used some bits of Velcro to keep it from moving around.
- Plug in the USB cord
You should now be ready to start coding the sketch
Define the pins the button and its LED will use:
const int buttonPin = 2; // Pin used to detect a button press
const int ledPin = 13; // Pin for the highside of the button LED
Setup a variable to track the state of the button:
// Variables used in interrupt service routines and other parts of the program should be volatile
// 2 is used to indicate there has not been an interrupt yet
// 1 is button released
// 0 is button pressed
volatile int buttonState = 2;
Initialize the pins and the interrupt to handle the button state change:
// setup the button and LED
pinMode(ledPin, OUTPUT); // will raise and lower this output to toggle the LED.
pinMode(buttonPin, INPUT); // will watch this pin to detect the button press.
// attaching and interrupt to the pin
attachInterrupt(buttonPin, pinIsr, CHANGE);
Code up the interrupt handling routine:
void pinIsr()
{
buttonState = digitalRead(buttonPin);
digitalWrite(ledPin, buttonState);
if (buttonState == 1)
canPlayQuip = true;
delayMicroseconds(200);
}
Recording the quipsI used the opensource tool Audacity to record all the audio in this project. I did this for 2 reasons:
- These are my own recording, so no copyright issues should arise.
- I like using Audacity and give them a shout out every chance I get
The thing to remember on the recordings is that when you save them they need to be in the following format:
- Constant bitrate
- 192kbps bitrate
- A few seconds long
There should be a way not to have these requirements. I believe you should be able to have a good number of bitrates. I tried a large number of options, but these are the only ones that would work with initialization settings of the player on the Spresense.
Here is the line that does that initialization:
/*
* Set main player to decode stereo mp3. Stream sample rate is set to "auto detect"
* Search for MP3 decoder in "/mnt/sd0/BIN" directory
*/
err = theAudio->initPlayer(AudioClass::Player0, AS_CODECTYPE_MP3, "/mnt/sd0/BIN", AS_SAMPLINGRATE_AUTO, AS_CHANNEL_MONO);
It didn't seem to matter what I had set for the sampling rate or channel type, the MP3s would not play correctly until used the bitrates as mentioned above.
Reading from the SD cardSetup a variable that will understand how to access the SD card:
SDClass theSD;
Setup a variable to hold File information:
File myDir;
Open the root directory on the SD card"
Serial.println("Reading available quips from the SD card...");
myDir = theSD.open("/");
Read in the files and put them in an array for processing later:
void getQuips(File dir, int numTabs)
{
String endTest = ".mp3";
while (true)
{
File entry = dir.openNextFile();
if (!entry || numQuips >= MAX_QUIPS)
{
// no more files
break;
}
String entryName = entry.name();
entryName.remove(0,1);
if (!entry.isDirectory() && entryName.endsWith(endTest) && !entryName.equals("init.mp3"))
{
foundQuips[numQuips] = entryName;
numQuips++;
}
entry.close();
}
}
Close the Directory.:
myDir.close();
You can only have one file open at a time. In this case, as with most filesystems, the Directory is just a special file on disk.
Make the sound playSetup a variable to hold the audio instance:
AudioClass *theAudio;
Setup the base of the audio:
void setupAudio()
{
puts("checking audio initialization");
// make sure we are not calling this if there is nothing to call
if(audioInitialized)
{
puts("shutting down the audio subsystem");
theAudio->end();
sleep(1);
audioInitialized = false;
}
// start audio system
theAudio = AudioClass::getInstance();
theAudio->begin(audio_attention_cb);
puts("initialization Audio Library");
/* Set clock mode to normal */
theAudio->setRenderingClockMode(AS_CLKMODE_NORMAL);
puts("setting player mode");
/* Verify player initialize */
if (err != AUDIOLIB_ECODE_OK)
{
printf("Player0 initialize error\n");
exit(1);
}
/* Main volume set to -16.0 dB */
theAudio->setVolume(60);
audioInitialized = true;
}
Figure out which quip to play:
if (canPlayQuip)
{
playQuip(foundQuips[(int)random(numQuips)]);
currentQuip++;
Serial.print("currentQuip: ");
Serial.println(currentQuip);
}
Setup the player to play the quip and play it:
void playQuip(String fileName)
{
canPlayQuip = false;
Serial.print("Playing: ");
Serial.println(fileName);
/* Open file placed on SD card */
File myFile = theSD.open(fileName);
/* Verify file open */
if (!myFile)
{
printf("File open error\n");
exit(1);
}
printf("Open! %s\n", myFile.name());
theAudio->setPlayerMode(AS_SETPLAYER_OUTPUTDEVICE_SPHP, AS_SP_DRV_MODE_LINEOUT);
puts("player initialization");
/*
* Set main player to decode stereo mp3. Stream sample rate is set to "auto detect"
* Search for MP3 decoder in "/mnt/sd0/BIN" directory
*/
err = theAudio->initPlayer(AudioClass::Player0, AS_CODECTYPE_MP3, "/mnt/sd0/BIN", AS_SAMPLINGRATE_AUTO, AS_CHANNEL_MONO);
/* Send first frames to be decoded */
err = theAudio->writeFrames(AudioClass::Player0, myFile);
printf("Error: %d\n", err);
if ((err != AUDIOLIB_ECODE_OK) && (err != AUDIOLIB_ECODE_FILEEND))
{
printf("File Read Error! =%d\n",err);
myFile.close();
exit(1);
}
puts("Play!");
theAudio->startPlayer(AudioClass::Player0);
delay(100);
puts("Stop!");
sleep(1);
theAudio->stopPlayer(AudioClass::Player0);
puts("closing file");
myFile.close();
puts("returning to ready mode");
theAudio->setReadyMode();
}
Make sure you close file when you are done as you can only have one open. Also, return the player to "Ready Mode" so that it is ready to play again.
DemoFurther thoughtsI think this a pretty neat board with a lot of possibilities. I was a bit surprised that there was no wireless connectivity built on the board. There are add-on boards that could solve this issue, and other projects in this section have shown how todo this. I do wish there was a way through the Arduino IDE to use more than one of the cores. It was a great learning experience.
I got to thinking of some other project that could be done using a similar approach as I did with this project. Here are a few of those ideas:
Location based answers
Connect to sensors and be able to give more creative answer with reading the sensor levels.
Put this in a dog collar so they can talk to youl.
Make a bunch of inspiring quips and have it go off due to some event in the team room to Inspire the Team to greatness!
Comments