Give your Shield Bot a voice! This project equips your Shield Bot to “speak” to people or objects it encounters while roaming around.
The robot uses the PING))) Ultrasonic Distance Sensor to check for obstacles within range. When it detects something, it stops and directs the Adafruit “Music Maker” MP3 shield to play WAV files stored on a microSD card, selecting different files if the obstacle moves out of the way or not.
Three WAV files with kitty noises are provided, but you can record your own to better reflect your Shield Bot’s unique personality! Of course, your robot could also respond with music, sound effects, animal calls, or whatever you’d like. Find all the project WAV files in our sound library, or you could reformat existing audio files to WAVs, or make your own WAVs using free Audacity software.
Requires parts not included in the base Arduino Shield-Bot kits. See parts list below for details. It is highly recommended that you complete the Shield-Bot tutorials before attempting this project.
You will also be using these items that are included in the Shield Bot Kit:
Did you know there is an Propeller C version of this project that uses our ActivityBot Robot? Click here to check it out!
Before you will be able to attach the Music Maker Shield to your Shield Bot, you will first have to solder headers to it. Follow the instructions posted here at Adafruit to solder headers to the Music Maker Shield: Adafruit Music Maker Shield Assembly (offsite). You will not have to solder the ICSP connector or speaker connectors.
Carefully align and press the Music Maker shield into the headers on top of the Parallax Shield for Arduino on the top of the Shield Bot:
You will need to download and copy the 3 .WAV files (“meow.wav”, “purrs.wav”, “hiss.wav”) to the microSD card using your computer and a microSD card reader/writer on or attached to your computer:
You will need to transfer the three WAV files above to the microSD. Use a microSD/USB card adapter or the microSD slot on your computer if it has one to load the files onto the SD card directly from your computer.
Now you are ready to program your KittyBot.
With this program, the KittyBot should roam around the room. If you stand in front of it, it should come to a stop and meow. If you step out of the way, it should purr and continue forward. If you do not move, it should hiss, then turn away and go another direction.
Let’s take a look at the code. You can adapt it to work with different sensors, and use your own WAV files to play different sounds.
The Arduino KittyBot project includes the following libraries. The three are standard Arduino Libraries. The fourth is a custom library for the Adafruit “Music Maker” MP3 Shield.
#include <SPI.h> // We will use the hardware SPI pins: CLK (13), MISO (12), MOSI (11) #include <Adafruit_VS1053.h> #include <SD.h> #include <Servo.h>
The two lines are used to set up two servo objects – one for each wheel:
Servo servoRight; Servo servoLeft;
The next lines of code define which pins are used by the different functions and objects in the KittyBot’s code:
// These are the pins used for the music maker shield #define SHIELD_RESET -1 // VS1053 reset pin (unused!) #define SHIELD_CS 7 // VS1053 chip select pin (output) #define SHIELD_DCS 6 // VS1053 Data/command select pin (output) // These are common pins between breakout and shield #define CARDCS 4 // Card chip select pin // DREQ should be an Int pin, see http://arduino.cc/en/Reference/attachInterrupt #define DREQ 3 // VS1053 Data request, ideally an Interrupt pin // The pin for the PING))) sensor #define pingPin A0 // The pins for the servos #define servoPinL 8 #define servoPinR 9
The PING))) sensor requires a few variables to be declared:
// establish variables for duration of the ping, // and the distance result in inches and centimeters: long duration, ping_inches, ping_cm;
Once the pins have been set up, we can set up the musicPlayer object:
// create shield-example object! Adafruit_VS1053_FilePlayer musicPlayer = Adafruit_VS1053_FilePlayer(SHIELD_RESET, SHIELD_CS, SHIELD_DCS, DREQ, CARDCS);
The setup function starts with a line of code that makes the piezospeaker beep when the KittyBot is powered up and so that you can hear if the robot resets while driving due to low batteries. The freqout function that gets called here is written out at the end of the code. Because we are also using a random number generator in the code, we read an unconnected analog pin to get a random number to seed the random number generator. Lastly, in the setup we start up the musicPlayer and servo objects:
void setup() { freqOut(2, 2000, 3000); // use an unconnected analog input to seed the random number generator (analogRead(2)); // initialise the music player musicPlayer.begin(); delay(100); // initialise the SD card SD.begin(CARDCS); // Set volume for left, right channels. lower numbers == louder volume! musicPlayer.setVolume(20,20); // DREQ is on an interrupt pin (on uno, #2 or #3) so we // can do background audio playing - not used in this example musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT); // DREQ int servoLeft.attach(servoPinL); // Attach left signal to pin 13 servoRight.attach(servoPinR); // Attach right signal to pin 12 }
The first thing the program does after it sets up is to read the PING))) sensor and calls a function to set the motors to drive forward:
void loop() { delay(50); ping(); drive_forward();
Next, the program checks the reading from the PING))) sensor to see if there is something in the way. If there is, it stops and calls .detach() to stop sending any updates to the servos. This is necessary to prevent the servos from jittering or “chattering”. It then plays the “meow.wav” file and waits for 4 seconds:
if (ping_cm < 21) { // is there an obstacle? // Stop moving and detach to prevent servo chatter drive_stop(); servoLeft.detach(); servoRight.detach(); delay(50); musicPlayer.playFullFile("meow.wav"); delay(4000);
After the 4 second delay, the KittyBot reads its PING))) sensor to see if the obstacle has moved out of the way. If it is still there, it plays the “hiss.wav file”, backs up slightly, and then turns a random amount right or left before starting all over again.
ping(); if (ping_cm < 21) { // is there still an obstacle? delay(50); musicPlayer.playFullFile("hiss.wav"); delay(100); // Attach servos and begin driving servoLeft.attach(servoPinL); servoRight.attach(servoPinR); drive_reverse(); delay(50); if(random(10)<5) { drive_turnLeft(); } else { drive_turnRight(); } //turn a random amount delay(random(300,800)); drive_stop(); delay(50);
If the obstacle did move out the way, the KittyBot plays the purrs.wav file and reattaches the servos. When the code loops back to the beginning, the call to drive_forward(); will make KittyBot move forward again.
The last section of code contains the functions for reading the PING))) sensor, setting the wheel speeds by setting the servos, and playing tones through the piezospeaker:
void ping() { // The PING))) is triggered by a HIGH pulse of 2 or more microseconds. // Give a short LOW pulse beforehand to ensure a clean HIGH pulse: pinMode(pingPin, OUTPUT); digitalWrite(pingPin, LOW); delayMicroseconds(2); digitalWrite(pingPin, HIGH); delayMicroseconds(5); digitalWrite(pingPin, LOW); // The same pin is used to read the signal from the PING))): a HIGH // pulse whose duration is the time (in microseconds) from the sending // of the ping to the reception of its echo off of an object. pinMode(pingPin, INPUT); duration = pulseIn(pingPin, HIGH); // convert the time into a distance // The speed of sound is 73.746 microseconds per inch (29 microseconds per centimeter). // Divide by 2 because it's a round-trip. ping_inches = duration / 74 / 2; ping_cm = duration / 29 / 2; return; } void drive_reverse() { servoLeft.writeMicroseconds(1700); // 1.7 ms -> counterclockwise servoRight.writeMicroseconds(1300); // 1.3 ms -> clockwise return; } void drive_forward() { servoLeft.writeMicroseconds(1300); // 1.3 ms -> clockwise servoRight.writeMicroseconds(1700); // 1.7 ms -> counterclockwise return; } void drive_turnLeft() { servoLeft.writeMicroseconds(1300); // 1.3 ms -> clockwise servoRight.writeMicroseconds(1300); // 1.3 ms -> clockwise return; } void drive_turnRight() { servoLeft.writeMicroseconds(1700); // 1.7 ms -> counterclockwise servoRight.writeMicroseconds(1700); // 1.7 ms -> counterclockwise return; } void drive_stop() { servoLeft.writeMicroseconds(1500); // 1.5 ms -> still servoRight.writeMicroseconds(1500); // 1.5 ms -> still return; } void freqOut(int tonePin, long toneDura, long toneFreq) { // used to play tone on startup pinMode(tonePin, OUTPUT); toneFreq = 500000L / toneFreq - 10L; long cycles = toneDura * 500L / toneFreq; for (long idx; idx <= cycles; idx++) { digitalWrite(tonePin, HIGH); delayMicroseconds(toneFreq); digitalWrite(tonePin, LOW); delayMicroseconds(toneFreq); } }