How Arduino KittyBot Works

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);
  }
}

 


Did You Know?

  • There is also a Propeller Activity Board version of this project.  Comparing the two will help you understand the similarities and differences between programming an Arduino and a Propeller.
  • You can record your own WAV files with Audacity, a free download for Mac and Windows.  See Creating Audio Files for WAV Players here.
  • To let your Shield Bot's own personality shine through, just use your own WAV files or use different WAV files than provided in the example code by replacing the file names in the musicPlayer function calls.  You might also want to lengthen or shorten the pauses to create different behaviors.
  • Get creative with this project; don't be afraid to try voices, animal noises, car noises, or any other sound effects you can come up with. The possibilities are endless!