This piggy bank does not want your money. Instead, when you insert money (or a block), he will immediately spit it out and run away from you. Following the ejection of the money, the piggy bank will turn 180 degrees using the hidden wheels beneath and navigate/dodge objects detected with its ultrasonic ranger. If an object is detected, the piggy bank will retreat backwards, then turn left or right in order to find a better path. If no objects are detected, it will continue running forward. This project makes use of a hobby servo, a single pole double throw switch, two DC motors with wheels, and an ultrasonic ranger. The box created was put together with slots and captive screws.

Solidworks files can be found here

See below for code:


/* Demo 5: Trickster for 16-223 by Jessica Lew and Jen Kwang
 *  
 * Putting money into a piggy bank will cause it to spit it out
 * and run away from you with its hidden wheels and dodge objects
 * in the way detected with ultrasoic ranger
 * 
 * Credit to Garth Zeglin for borrowed code from ultrasonic ranger
 * tutorial and Dual DC Motor Driver tutorial.  More info can 
 * respectively found in the following links:
 * https://courses.ideate.cmu.edu/16-223/f2018/text/ex/Arduino/DRV8833-motor-driver/DRV8833-motor-driver.html?highlight=motor
 * https://courses.ideate.cmu.edu/16-223/f2018/text/ex/Arduino/read-sonar/read-sonar.html?highlight=ultrasonic%20range
 */
#include <Servo.h> 
#define SWITCH_PIN 2
#define MOT_A1_PIN 5
#define MOT_A2_PIN 6
#define MOT_B1_PIN 9
#define MOT_B2_PIN 11

const int servoPin = 10;
const int TRIG_PIN = 8;
const int ECHO_PIN = 7;
const int MAX_DISTANCE = 450;
const long SOUND_SPEED = 34000;
const long TIMEOUT = (2 * MAX_DISTANCE * 1000000)/SOUND_SPEED;
bool run_state = false;
int angle = 180;
int count = 0;
bool left = true;
Servo svo;

void setup()
{

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(SWITCH_PIN, INPUT);
  pinMode(TRIG_PIN, OUTPUT);
  digitalWrite(TRIG_PIN, LOW);
  pinMode(ECHO_PIN, INPUT);

  pinMode(MOT_A1_PIN, OUTPUT);
  pinMode(MOT_A2_PIN, OUTPUT);
  pinMode(MOT_B1_PIN, OUTPUT);
  pinMode(MOT_B2_PIN, OUTPUT);

  // Start with drivers off, motors coasting.
  digitalWrite(MOT_A1_PIN, LOW);
  digitalWrite(MOT_A2_PIN, LOW);
  digitalWrite(MOT_B1_PIN, LOW);
  digitalWrite(MOT_B2_PIN, LOW);

  Serial.begin(9600);
  
  svo.attach(servoPin);
  svo.write(angle);
}

void set_motor_pwm(int pwm, int IN1_PIN, int IN2_PIN)
{
  if (pwm < 0) {  // reverse speeds
    analogWrite(IN1_PIN, -pwm);
    digitalWrite(IN2_PIN, LOW);

  } else { // stop or forward
    digitalWrite(IN1_PIN, LOW);
    analogWrite(IN2_PIN, pwm);
  }
}

void set_motor_currents(int pwm_A, int pwm_B)
{
  set_motor_pwm(pwm_A, MOT_A1_PIN, MOT_A2_PIN);
  set_motor_pwm(pwm_B, MOT_B1_PIN, MOT_B2_PIN);

  // Print a status message to the console.
  Serial.print("Set motor A PWM = ");
  Serial.print(pwm_A);
  Serial.print(" motor B PWM = ");
  Serial.println(pwm_B);
}

void spin_and_wait(int pwm_A, int pwm_B, int duration)
{
  set_motor_currents(pwm_A, pwm_B);
  delay(duration);
}

void loop()
{  
  int value = digitalRead(SWITCH_PIN);
  digitalWrite(LED_BUILTIN, value);
  Serial.print("Switch value:  ");
  Serial.print(value);

  long duration = ping_sonar();
  float distance = (duration * 1e-6 * SOUND_SPEED) / 2;

  // push switch or feed the piggy bank
  if (value == 1 && run_state == false){
    delay(1000);
    svo.write(angle - 100);
    delay(500);
    svo.write(angle);
    delay(500);
    run_state = true;
  }

  if (duration > 0) {
    Serial.print("  Ping: ");
    Serial.print(duration);
    Serial.print(" usec   Distance: ");
    Serial.print(distance);
    Serial.println(" cm");

  } else {
    Serial.println("No ping.");
  }
  
  // after fed, runs backwards, spins 180 deg, then a bit forward.
  if (run_state && count == 0){
    spin_and_wait(-255, 255, 300);
    spin_and_wait(0, 0, 500);
    spin_and_wait(255, 255, 2000);
    spin_and_wait(255, -255, 600);
  }

  // there's an object in the way
  if (count <= 20 && run_state && distance <= 20) {
    // move backwards then right or left
    spin_and_wait(-255, 255, 500);
    if (left == false){
      spin_and_wait(255, 255, 1000);
      spin_and_wait(0, 0, 2000);
      left = true;
    }
    else if (left == true) {
      // right/left
      spin_and_wait(-255, -255, 1000);
      spin_and_wait(0, 0, 2000);
      left = false;
    }
    count = count + 1;
  }
  
  // there's no object in the way, move a few steps
  else if (count <= 20 && run_state && distance > 20) {
    spin_and_wait(255, -255, 600);
    spin_and_wait(0, 0, 2000);
    count = count + 1;
  }

  //after done running away, reset code.
  if (count >= 20) {
    run_state = false;
    count = 0;
  }
  delay(30);
}

long ping_sonar(void)
{
  // Generate a short trigger pulse.
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);

  // Measure the pulse length
  return pulseIn(ECHO_PIN, HIGH, TIMEOUT);
}