AUTOMATIC PET FOOD DISPENSER

by GillesDM in Circuits > Raspberry Pi

1153 Views, 3 Favorites, 0 Comments

AUTOMATIC PET FOOD DISPENSER

GILLES_PETFEED.png

Ever felt like wasting too much time feeding your pet? Ever had to call someone to feed your pets while you were on a holiday? I have tried to fix both these issues with my current school project: Petfeed!

Supplies

Raspberry Pi 3b

Bar Load Cell (10kg)

HX711 Load Cell Amplifier

Waterlevel Sensor (https://www.dfrobot.com/product-1493.html)

Ultrasonic Proximity Sensor

LCD 16-pins

2x stepper motor 28byj-48

2x stepper motor driver ULN2003

Wiring

Electronics.jpeg
Final_fritzing_bb.png

lots of cabling here. Get out your jumper cables and start pinning!

Make Your Load Cell Usable

loadcell.jpg

to use the load cell, we first need to attach it to two plates: a bottom plate, and a plate on which we will weigh our food.

The screws you need are a pair of M4 screws with matching bolts and a pair of M5 screws with matching bolts. I used a small drill to make the holes.

(pic: https://tutorials-raspberrypi.com/digital-raspberry-pi-scale-weight-sensor-hx711/)

Normalised Database

EER _V2.PNG

data from our sensors has to be saved in a database. For the python files to connect to the database: see below.

then you also need a config file:

[connector_python]<br>user = *yourusername*
host = 127.0.0.1 #if local
port = 3306
password = *yourpassword*
database = *yourdb*

[application_config]
driver = 'SQL Server'

Coding the Load Cell

import RPi.GPIO as GPIO<br>import threading
import time

from hx711 import HX711
from helpers.stepperFood import StepperFood
from helpers.LCDWrite import LCDWrite
from repositories.DataRepository import DataRepository <br>

After importing all of our libraries (note, we're using the HX711 Library to drive the load cell) we can start writing our actual code

TARRA_CONSTANT = 80600
GRAM_CONSTANT = 101 

For finding out our constants, first set TARRA_CONSTANT = 0 and GRAM_CONSTANT = 1.

Next we need to find out the value our load cell reads when theres nothing being weighed. This value will be TARRA_CONSTANT.

As for GRAM_CONSTANT, simply take an object you know the weight of (i used a pack of spaghetti's), weigh it and divide the load cell readout with the actual weight of the object. For me this was 101.

class LoadCell(threading.Thread):
    def __init__(self, socket, lcd):
        threading.Thread.__init__(self)
        self.hx711 = HX711(
            dout_pin=5,
            pd_sck_pin=6,
            channel='A',
            gain=64
        )
        self.socket = socket
        self.lcd = lcd 

here we initialize the LoadCell class and map the pins.

    def run(self):
        try:
            while True:
                self.hx711.reset()   # Before we start, reset the HX711 (not obligate)
                measures_avg = sum(self.hx711.get_raw_data()) / 5
                weight = round(
                    (measures_avg - TARRA_CONSTANT) / GRAM_CONSTANT, 0)
                print("weight: {0}".format(weight))
                DataRepository.insert_weight(weight)
                data_weight = DataRepository.get_data_sensor(3)
                historyId = data_weight["SensorsHistory"]
                db_weight = data_weight["value"]
                actionTime = data_weight["actionTime"]
                self.socket.emit('data_weight', {
                    "id": historyId, "Weight": db_weight, "Time": DataRepository.serializeDateTime(actionTime)})
                print("zou moeten emitten")
                writeWeight = "weight: " + str(db_weight)
                msg = "PETFEED"
                LCDWrite.message()
                if int(db_weight[:-2]) <= 100:
                    StepperFood.run()
		time.sleep(20)


        except Exception as e:
            print("Error with weighing" + str(e))

Coding the Water Sensor

import time<br>import threading
from repositories.DataRepository import DataRepository
from RPi import GPIOGPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO_Water = 18
GPIO.setup(GPIO_Water, GPIO.IN)

class WaterSensor(threading.Thread):
    def __init__(self, socket):
        threading.Thread.__init__(self)
        self.socket = socket
        self.vorige_status = 0

def run(self):
        try:
            while True:
                water = self.is_water()
                print(water)
                status = water["status"]
                action = water["action"]
                DataRepository.insert_water(str(status), action)
                data_water = DataRepository.get_data_sensor(2)
                historyId = data_water["SensorsHistory"]
                value = data_water["value"]
                if value == "0":
                    value = "te weinig water"
                else:
                    value = "genoeg water"
                actionTime = data_water["actionTime"]
                self.socket.emit('data_water', {
                    "id": historyId, "value": value, "Time": DataRepository.serializeDateTime(actionTime), "action": action})
                time.sleep(5)        

	except Exception as ex:
            print(ex)
            print('error bij watersensor')

    def is_water(self):
        status = GPIO.input(GPIO_Water)


        if self.vorige_status == 0 and status == 1:
            print('water gedetecteerd')
            sensorData = {"status": status, "action": "water gedetecteerd"}
            self.vorige_status = status
            status = GPIO.input(GPIO_Water)

        if self.vorige_status == 1 and status == 1:
            print('water aanwezig')
            sensorData = {"status": status, "action": "water aanwezig"}
            status = GPIO.input(GPIO_Water)

        if self.vorige_status == 1 and status == 0:
            print('water weg')
            sensorData = {"status": status, "action": "water weg"}
            self.vorige_status = status
            status = GPIO.input(GPIO_Water)

        if self.vorige_status == 0 and status == 0:
            print('startpositie')
            status = GPIO.input(GPIO_Water)
            sensorData = {"status": status, "action": "startpositie"}
        return sensorData

Coding the Proximity Sensor

import time<br>import threading
from repositories.DataRepository import DataRepository
from RPi import GPIO

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO_Trig = 4
GPIO_Echo = 17

GPIO.setup(GPIO_Trig, GPIO.OUT)
GPIO.setup(GPIO_Echo, GPIO.IN)

def current_milli_time(): return int(round(time.time() * 1000))

class UltrasonicSensor(threading.Thread):
    def __init__(self, socket):
        threading.Thread.__init__(self)
        self.socket = socket

    def run(self):
        try:
            last_reading = 0
            interval = 5000
            while True:
                if current_milli_time() > last_reading + interval:
                    dist = self.distance()
                    print("Measured Distance = %.1f cm" % dist)
                    DataRepository.insert_proximity(dist)
                    data_prox = DataRepository.get_data_sensor(1)
                    historyId = data_prox["SensorsHistory"]
                    prox = data_prox["value"]
                    actionTime = data_prox["actionTime"]
                    self.socket.emit('data_proximity', {
                        "id": historyId, "Proximity": prox, "Time": DataRepository.serializeDateTime(actionTime)})
                    last_reading = current_milli_time()

        except Exception as ex:
            print(ex)

    def distance(self):
        # set Trigger to HIGH
        GPIO.output(GPIO_Trig, True)

        # set Trigger after 0.01ms to LOW
        time.sleep(0.00001)
        GPIO.output(GPIO_Trig, False)

        StartTime = time.time()
        StopTime = time.time()

        # save StartTime
        while GPIO.input(GPIO_Echo) == 0:
            StartTime = time.time()

        # save time of arrival
        while GPIO.input(GPIO_Echo) == 1:
            StopTime = time.time()

        # time difference between start and arrival
        TimeElapsed = StopTime - StartTime
        # multiply with the sonic speed (34300 cm/s)
        # and divide by 2, because there and back
        distance = (TimeElapsed * 34300) / 2

        return distance

Coding the Stepper Motors

import RPi.GPIO as GPIO<br>import time
import threading

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

control_pins = [12, 16, 20, 21]

for pin in control_pins:
    GPIO.setup(pin, GPIO.OUT)
    GPIO.output(pin, 0)

halfstep_seq = [
    [1, 0, 0, 0],
    [1, 1, 0, 0],
    [0, 1, 0, 0],
    [0, 1, 1, 0],
    [0, 0, 1, 0],
    [0, 0, 1, 1],
    [0, 0, 0, 1],
    [1, 0, 0, 1]
]

class StepperFood:
    # 512 is 1 keer rond

    def run():
        for i in range(512):
            for halfstep in range(8):
                for pin in range(4):
                    GPIO.output(control_pins[pin], halfstep_seq[halfstep][pin])
                time.sleep(0.001)

        for pin in range(4):
            GPIO.output(control_pins[pin], 0) 

This code is reusable for the other stepper motor, just set the control pin numbers to their repective pins and rename the class to StepperWater:

Coding the LCD

Lots of code, but we're almost done.

The LCD class is included as file LCD.py

from helpers.LCD import LCD

E = 26
RS = 25

D0 = 19
D1 = 13
D2 = 24
D3 = 22
D4 = 23
D5 = 8
D6 = 7
D7 = 10

lcd = LCD(E, RS, [D0, D1, D2, D3, D4, D5, D6, D7])

class LCDWrite:

    def message(msg):
        try:
            print("try")
            lcd.init_LCD()
            lcd.send_instruction(12)
            lcd.clear_display()
            lcd.write_message(msg, '1')
        except:
            print("error LCDWrite")

Downloads

The End

PETFEED_Schets_GillesDeMuynck.jpeg
PETFEED_AANDUIDING.jpg

final result: how we drew it up vs. how it ended up.