Project showcase
Herbert the Digidog

Herbert the Digidog

An autonomous dog pet. Play and have fun with out having to deal with a messy animal.

  • 2,244 views
  • 1 comment
  • 21 respects

Components and supplies

Ardgen mega
Arduino Mega 2560 & Genuino Mega 2560
×1
Pixy
×1
grove motor driver
×1

Necessary tools and machines

Glue Gun
Balsa Wood
fur (if you want it)

Apps and online services

About this project

 ( built for the DC hackathon)


Herbert is a robotic virtual pet. A companion which allows you to enjoy the pleasures of a real dog without the mess . He will chase objects of certain colors and change his mood depending on your interaction with him. Hebert has mood and energy levels that you can tell based on his eyes. The brighter the eyes, the more energy. The color dictates his current mood. Red-Mad; Purple-Happy; Blue-Indifferent; Green-Excited; Yellow-Annoyed. 

Chassis :


We made our chassis out of thin balsa wood. One long strip big enough to hold an Arduino mega and a battery pack. It had one inch high sides to hold the components. The chassis can really be anything, as long as it has space and places for lights/eyes and a pixy cam. WARNING! Do not use metal as a chassis unless you have the circuit boards protected. We made a removable back side so you can access the hardware using magnets and washers. You will want to put fur on, but if you do make sure you leave a spot for the fetch button to be and a place for the touch sensor/Petting spot.  Use hot glue or wood glue to put the balsa together to make the chases of your digipet!


Hardware:


After setting up the Pixy to work with the Arduino, be sure to define what you want for your 'food' object and 'ball' object to be. Currently 'food' is the 0 index object of the block array, and 1 index is the ball. This can be easily changed with-in the code listed below.   
We used a grove shield to connect touch sensors, buttons, and our motor drivers. The touch sensors are for petting our dog and our button allows us to control when the dog will attempt to fetch a ball. The grove motor driver connects directly to the I2C port on the grove shield. 

Our pixy camera connects into the icsp port on the grove shield.LEDs connect directly to the GPIO pins of the Arduino. Pin references can be found within the code. 


Features:

Our code uses the LEDs as a way to indicate mood.  Red means angry and green means happy.  It will go through a lot of different colors to.  We used the touch sensor as a way to make his happiness go back up if he gets angry.  It's almost like your petting him. Also, the lights will dim as he gets tired. The pet will also drive towards the location of his ball when in 'fetch mode' . 


Thanks for all of the support!

#hardware weekend


Code

MainC/C++
/*
    Herbet the Digidino v1.0
  by: Noah Edmiston , Roger Edmiston, Joe Schlee, and Paul Binder.
*/
#include <SPI.h>  
#include <Pixy.h>
#include <Wire.h>

#define MotorSpeedSet             0x82
#define PWMFrequenceSet           0x84
#define DirectionSet              0xaa
#define MotorSetA                 0xa1
#define MotorSetB                 0xa5
#define Nothing                   0x01
#define I2CMotorDriverAdd         0x0f  

int healthCheckInterval = 50;   // in millis
long previousMillis = 0;        // used to store the previous time check
uint16_t blocks = 0;

// Fetch button
const int buttonPin = 8;
boolean isFetching = false;     // set to true if in a fetching state

// Pet Touch
const int petPin = 7;
boolean isPetting = false;      // set to true if is in a petting state

boolean isEating = false;       // set to true if currently eating

Pixy pixy;

void setup()  {
  Wire.begin(); 
  pixy.init();
  delayMicroseconds(10000);
  Serial.begin(9600);
  Serial.println("setup begin");
  setupMoodAndEnergy();
  pinMode(buttonPin, INPUT);
  pinMode(petPin, INPUT);
}

void loop()  {
  // Get time value
  unsigned long currentMillis = millis();

  blocks = pixy.getBlocks();

  // check for Fetch button press
  int buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH && isFetching == false) 
  {
    Serial.println(buttonState);
    Serial.println("fetching the ball.");
    isFetching = true;
  }
  else if (buttonState == HIGH && isFetching == true)
  {
    isFetching = false;
  }

  if (isFetching == true)
  {
    fetchBall(); // do another round of trying to fetch the ball
    Serial.println("Still fetching.");
  }
  
  // check for food
  checkForFood();
  
  // check for Pet touch sensor (pet the cute robo dino)
  int petSensorValue = digitalRead(petPin);
  if (petSensorValue == 1 && isPetting == false)
  {
    //Serial.println(petSensorValue);
    isPetting = true;
  }

  // Determine health changes (mood and energy)
  if(currentMillis - previousMillis > healthCheckInterval)
  {
    previousMillis = currentMillis;
  
    // Determine mood LED
    determineMood();
    // adjust mood
    adjustMood();
    // adjust energy
    adjustEnergy();
    
    // reset any states related to health
    isPetting = false;
    isEating = false;
    
  } //end health check interval
  
}
Mood & Energy MethodsC/C++
Tab within the Main Arduino Solution
/*
Mood & Energy Methods

Will contain all the methods that control Herbet's mood and energy. 
*/

// Health Variables
extern int moodRedPin = 10;            // LED red pin for Eyes
extern int moodGreenPin = 11;          // LED green pin for Eyes
extern int moodBluePin = 12;           // LED Blue pin for Eyes
extern float moodValue = 65;          // current Mood value (0 - 100)
extern float moodChangeFactor = 0.1;   // degrade mood by this much over time with each health interval
extern float energyChangeFactor = 0.1; // degrade by this much over time with each health interval
extern float energyValue = 100;        // current Energy (0 - 100)
extern float foodChangeFactor = 2.0;   // increase Energy by this much over time when eating
extern boolean isSleeping = false;     // set to true if currently sleeping

void setupMoodAndEnergy()
{
  pinMode(moodRedPin, OUTPUT);
  pinMode(moodGreenPin, OUTPUT);
  pinMode(moodBluePin, OUTPUT);  
}

void determineMood()
{
    if (moodValue >= 90)
    {
      setColor(0,energyValue * 2.55,0); //green
    }
    else if (moodValue >= 70 && moodValue < 90)
    {
      setColor(energyValue * 2.55, 0, energyValue * 2.55); //purple
    }
    else if (moodValue >= 60 && moodValue < 70)
    {
      setColor(0, 0, energyValue * 2.55); //blue
    }
    else if (moodValue >= 50 && moodValue < 60)
    {
      setColor(energyValue * 2.55, energyValue * 2.55, 0); //yellow
    }
    else if (moodValue >= 40 && moodValue < 50)
    {
      setColor(energyValue * 2.55, energyValue * 1.02, energyValue * 1.78); //orange
    }
    else if (moodValue < 40)
    {
      setColor(energyValue * 2.55, 0, 0); //red
    }
}

void adjustMood()
{
    moodValue = moodValue - moodChangeFactor;  //time degrades mood
    //if isSleeping then increase moodValue by a factor
    //if isFetching then increase moodValue by a factor
    //if isEating then increase moodValue by a factor
    if (isPetting == true)
    {
      moodValue = moodValue + .5;
    }
    if (isFetching == true)
    {
      moodValue = moodValue + .3;
    }
    if (isEating == true)
    {
      moodValue = moodValue + .2;
    }
    if (moodValue < 0)
    {
      moodValue = 0;
    }
    if (moodValue > 100)
    {
      moodValue = 100;
    }
    //Serial.print("mood: ");
    //Serial.println(moodValue);
}

void  adjustEnergy()
{
    energyValue = energyValue - energyChangeFactor;  //time degrades energy
    //if isSleeping then increase energyValue by a factor
    //if isFetching then decrease energyValue by a factor
    //if isEating then increase energyValue by a factor
    //if isPetting has no impact on energy
    if (isEating == true)
    {
      energyValue = energyValue + foodChangeFactor;
    }
    if (isFetching == true)
    {
      energyValue = energyValue - 0.2;
    }
    if (energyValue < 0)
    {
      energyValue = 0;
    }
    if (energyValue > 100)
    {
      energyValue = 100;
    }
    //Serial.print("Energy: ");
    //Serial.println(energyValue);
}

void checkForFood()
{
  static int food = 0;
  //uint16_t blocks = 0;
  
  //blocks = pixy.getBlocks();  // commented out for debugging without camera
  
  if (blocks)
  {
    if (pixy.blocks[food].signature > 0)
    {
      isEating = true;
    }
  }
}

void setColor(int red, int green, int blue)
{
  /*Serial.print("LED change: ");
  Serial.print(red);
  Serial.print(", ");
  Serial.print(green);
  Serial.print(", ");
  Serial.println(blue); */
  analogWrite(moodRedPin, red);
  analogWrite(moodGreenPin, green);
  analogWrite(moodBluePin, blue);  
}
Motor Driver MethodsC/C++
Tab within the Main Arduino Solution
/*
Motor Driver Methods

Will contain all the methods Herbet uses to move about. 
*/

void MotorSpeedSetAB(unsigned char MotorSpeedA , unsigned char MotorSpeedB)  {
  MotorSpeedA=map(MotorSpeedA,0,100,0,255);
  MotorSpeedB=map(MotorSpeedB,0,100,0,255);
  Wire.beginTransmission(I2CMotorDriverAdd);
  Wire.write(MotorSpeedSet);       
  Wire.write(MotorSpeedA);             
  Wire.write(MotorSpeedB);              
  Wire.endTransmission();    
}
//set the prescale frequency of PWM, 0x03 default;
void MotorPWMFrequenceSet(unsigned char Frequence)  {    
  Wire.beginTransmission(I2CMotorDriverAdd); 
  Wire.write(PWMFrequenceSet);       
  Wire.write(Frequence);             
  Wire.write(Nothing);            
  Wire.endTransmission();   
}
//set the direction of DC motor. 
void MotorDirectionSet(unsigned char Direction)  {     
  Wire.beginTransmission(I2CMotorDriverAdd);
  Wire.write(DirectionSet);        
  Wire.write(Direction);             
  Wire.write(Nothing);             
  Wire.endTransmission();    
}

void MotorDriectionAndSpeedSet(unsigned char Direction,unsigned char MotorSpeedA,unsigned char MotorSpeedB)  {  
  MotorDirectionSet(Direction);
  MotorSpeedSetAB(MotorSpeedA,MotorSpeedB);  
}

int getSpeed(int xValue)
{
  static int speedCap = 25;
  static int offSet = 195;
  int returnValue = 0;
  
  returnValue = xValue - offSet;
  
  if (returnValue < 0)
  {
    returnValue = returnValue - returnValue - returnValue;
  }
  
  if (returnValue > speedCap)
  {
    returnValue = speedCap;
  }
  return returnValue;
}
Play MethodsC/C++
Tab within the Main Arduino Solution
/*
Play Methods.

Will contain all the methods Herbet uses to play different games. 
*/

/*
pixy.blocks[i].signature The signature number of the detected object (1-7)
pixy.blocks[i].x The x location of the center of the detected object (0 to 319)
pixy.blocks[i].y The y location of the center of the detected object (0 to 199)
pixy.blocks[i].width The width of the detected object (1 to 320)
pixy.blocks[i].height The height of the detected object (1 to 200)
pixy.blocks[i].print() A member function that prints the detected object information to the serial port
*/

void fetchBall()
{   
 //Pixy test
  static int i = 0;
  static int ball = 1;
  static int minRange = 120;
  static int maxRange = 220;
  static int turnSpeed = 50;
  
  int j;
  //uint16_t blocks = 0;
  char buf[32]; 

  //blocks = pixy.getBlocks();
  
  if (blocks)
  {
    //Serial.println(pixy.blocks[ball].width);
    sprintf(buf, "Detected %d:\n", blocks);
    Serial.println(buf);
    Serial.println(pixy.blocks[ball].x);
    pixy.blocks[ball].print();
    MotorSpeedSetAB(0,0);
    delay(10);
    MotorDirectionSet(0b0000);
    
    if (pixy.blocks[ball].width >= 50) // stop fetching once it is close enough to have "caught it"
    {
      isFetching = false;
      Serial.println("done fetching the ball.");  
      MotorSpeedSetAB(0,0);
      delay(10);
    }
    else if (pixy.blocks[ball].x > maxRange)
    {
       Serial.println("turn right");
       //MotorSpeedSetAB(getSpeed(pixy.blocks[ball].x),getSpeed(pixy.blocks[ball].x));
       MotorSpeedSetAB(5, turnSpeed);
       delay(20); 
       MotorDirectionSet(0b1010);  
    }
    else if (pixy.blocks[ball].x < minRange)
    {
       Serial.println("turn left");
       //MotorSpeedSetAB(getSpeed(pixy.blocks[ball].x),getSpeed(pixy.blocks[ball].x));
       MotorSpeedSetAB(turnSpeed, 5);
       delay(20); 
       MotorDirectionSet(0b1010);                           
    }
    else if (pixy.blocks[ball].width < 50)
    {
      Serial.println("FORWARDS");
      MotorSpeedSetAB(80,80);
      delay(10);
      MotorDirectionSet(0b1010);      
    } 

    // run the set direction for a bit then stop motor
    delay(300); 
    MotorSpeedSetAB(0,0);
  }
  else
  {
    MotorSpeedSetAB(0,0);
  }
}

Schematics

Arduino Mega Scematic
Large arduino board.
Pixy Cam

Comments

Similar projects you might like

How to Build Sauron The Lord of the Robots

Project tutorial by Mahmoud Ahmed

  • 6,730 views
  • 5 comments
  • 26 respects

Simple Programmable Robotic Arm

Project showcase by Ryan Chan

  • 85,453 views
  • 75 comments
  • 219 respects

360 Radar (that encodes data onto an image)

Project tutorial by Alireza Karkon

  • 11,154 views
  • 10 comments
  • 43 respects

Talking Raven - Animatronic for Halloween

Project showcase by pibyte

  • 5,315 views
  • 2 comments
  • 21 respects

Rana: An arduino inspired biological robot

Project showcase by Stephane Hatgis-kessell

  • 2,613 views
  • 3 comments
  • 6 respects

Interactive Donation Box

Project showcase by Ian McKay

  • 2,612 views
  • 0 comments
  • 8 respects
Add projectSign up / Login