Evan Rust
Published © GPL3+

IoT Treat Dispenser For Pets

With the push of a button you can dispense treats and watch your pet in real time without having to be in the same room.

IntermediateFull instructions provided6 hours23,209

Things used in this project

Hardware components

DFRobot Raspberry Pi 3
×1
DFRobot Raspberry Pi Camera Module
×1
DFRobot Stepper Motor with Planetary Gear
×1
I2C LCD 16x2
×1
Barrel Jack to Terminal
×1
DRV8825 Stepper Motor Driver
×1
Capacitor 100 µF
Capacitor 100 µF
×1
Arduino UNO
Arduino UNO
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Software apps and online services

Arduino IDE
Arduino IDE
Notepad++
Flask
Fusion 360
Autodesk Fusion 360

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
CNC Router

Story

Read more

Custom parts and enclosures

Thingiverse Link

Schematics

Schematic

Code

Python Code

Python
from flask import Flask, request
import subprocess
import shlex
from serial import Serial

rpi_ip_addr = "192.168.0.2"
arduino_addr = "/dev/ttyACM0"

ser = Serial(arduino_addr, baudrate=9600)

p = subprocess.Popen(shlex.split('./mjpg_streamer -o "output_http.so -w ./www" -i "input_raspicam.so"'))

app = Flask(__name__)

@app.route('/')
def treat_ready():
    return '<html><head><title>Treat Dispenser</title></head>'+ \
    '<body><form action="/dispense" method="post"><input type="submit" name="submit" value="Get some treats">'+ \
    '</form></body></html>'
    
@app.route('/dispense', methods=['POST','GET'])
def dispense_treat():
    if request.method == 'POST':
        if request.form['submit'] == "Get some treats":
            ser.write('D\n')
            return '<html><head><title>Dispensing</title></head><body><h1>Dispensing treats!'+\
                '</h1><a href="/">Head Back</a></body></html>'
        
if __name__=="__main__":
    try:
        app.run(host='0.0.0.0')
    except KeyboardInterrupt:
        p.terminate()
        ser.close()

Arduino Uno Code

C/C++
//Stepper Board Test Code
//Kevin Darrah  2017

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x20,16,2);

const int stepPin = 2;//only works on this pin right now
const int dirPin = 6;
const int actPin = 4;//not used
const float motorAngle = 1.8;
const float stepSize = 1;//full=1, half=0.5, quarter=0.25, etc...


void stepperRotate(float rotation, float rpm);

void setup() {
  // put your setup code here, to run once:
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  //pinMode(actPin, OUTPUT);  hooked to VCC, so no Arduino control
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  delay(5000);
  lcd.clear();
  lcd.home();
  lcd.print(" IoT Pet Treat");
  lcd.setCursor(0,1);
  lcd.print("   Dispenser");

}

void loop() {
  if(Serial.available()>0){
    String data = Serial.readStringUntil('\n');
    if(data=="D"){
      lcd.clear();
      lcd.home();
      lcd.print("Dispensing");
      lcd.setCursor(0,1);
      lcd.print("treats now.");
      stepperRotate(.5, 30);//rotations, RPM
      delay(500);
      stepperRotate(-.2, 30);//rotations, RPM
      delay(3000);
      lcd.clear();
      lcd.home();
      lcd.print(" IoT Pet Treat");
      lcd.setCursor(0,1);
      lcd.print("   Dispenser");
    }
  }
  delay(1500);

}

void stepperRotate(float rotation, float rpm) {
  if (rotation > 0) {
    digitalWrite(dirPin, HIGH);
  }
  else {
    digitalWrite(dirPin, LOW);
    rotation = rotation * -1;
  }

  float stepsPerRotation = 1050;

  //now we have the steps per rotation, multiply by the rotations for this command to get total steps
  float totalSteps = rotation * stepsPerRotation;

  unsigned long stepPeriodmicroSec = ((60.0000 / (rpm * stepsPerRotation)) * 1E6 / 2.0000) - 5;

  for (unsigned long i = 0; i < totalSteps; i++) {
    PORTD |= (1 << 2);
    delayMicroseconds(stepPeriodmicroSec);
    PORTD &= ~(1 << 2);
    delayMicroseconds(stepPeriodmicroSec);
  }

}

Credits

Evan Rust

Evan Rust

120 projects • 1054 followers
IoT, web, and embedded systems enthusiast. Contact me for product reviews or custom project requests.

Comments