Hardware components | ||||||
| × | 2 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
|
Our project is making a dog feeder that can be fully automated and is also usable for multiple pets, right now our project is doing its first generation, doing some prove of concept things as well as exploring the hardware capabilities and limitation
This started from my friend having multiple pet dogs and he stays away from home rather often. Due to the fact that he needed to work overtime often, he tasked us to make a system that can help feed his dogs together having just one feeder.
Hence, to resolve my friend (and other pet owner)'s problems, we have decided to make an automatic pet feeder that:
- ensure the dogs are well catered for and receives proper amount of food
- has to be adjustable to cater to different dog weights
- checks the food should also be checked if it is safe for consumption.
- notifies owner if feed is low
- able to clarify which dog wants to be fed
- Pet under/overeating can lead to higher risk of illness.
- Adult dogs should be eating approximately 30 grams of food per kg per day.
- According to European Pet Food Industry Federation (FEDIAF), a year supply of wet dog food one pet has a footprint of approximately 464kg of carbon dioxide, meaning pet food should not be wasted at all.
from m5stack import *
from m5ui import *
from uiflow import *
import urequests
import unit
import time
import network
import gc
import espnow
import wifiCfg
WIFI_SSID = 'thor3'
WIFI_PASSWORD = 'MinervaDingo2isGreat'
Google_URL = 'https://script.google.com/macros/s/AKfycbwJTZhIClCpwP7IN2EZdQq6NHdjQA0wA7Q0PJ6LWY-jfYm9vRujlW26sTSOcIzi21S2LA/exec'
Data_send = None
wifi = network.WLAN(network.STA_IF)
wifiCfg.wlan_ap.active(True)
wifiCfg.wlan_sta.active(True)
espnow.init()
setScreenColor(0x222222)
servo_1 = unit.get(unit.SERVO, unit.PORTB)
pahub_0 = unit.get(unit.PAHUB, unit.PORTA)
rfid_0 = unit.get(unit.RFID, unit.PAHUB0)
env2_0 = unit.get(unit.ENV2, unit.PAHUB1)
tof_0 = unit.get(unit.TOF, unit.PAHUB2)
id1 = None
id2 = None
label0 = M5TextBox(10, 10, "Status:", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label1 = M5TextBox(10, 40, "RFID ID:", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label2 = M5TextBox(10, 70, "ID1:", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label3 = M5TextBox(10, 100, "ID2:", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label4 = M5TextBox(10, 130, "Distance:", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label5 = M5TextBox(10, 160, "Temperature:", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label6 = M5TextBox(10, 190, "Humidity:", lcd.FONT_Default, 0xFFFFFF, rotate=0)
url_sent = False
id1_timer_start = None
id2_timer_start = None
timer_duration = 10
SAFE_TEMP = 30
SAFE_HUM = 100
label0.setText('send request')
def send_request(url):
try:
max_redirects = 5
redirects_count = 0
while redirects_count < max_redirects:
req = urequests.request(method='GET', url=url, headers={'Content-Type': 'text/html'})
if req.status_code == "Redirects not yet supported":
label0.setText('succeeded')
print(full_url)
break
except Exception as e:
label0.setText('succeeded')
print("Exception:", e)
print(full_url)
def send_cb(flag):
try:
global Data_send
Data_send = flag
label1.setText('Yes')
print("Received ESP-NOW data:", flag)
pass
except Exception as e:
print("Exception:", e)
espnow.send_cb(send_cb)
espnow.add_peer('98:f4:ab:6b:a2:65', id=1)
label0.setText(str(Data_send))
Data_send = None
while True:
dis = min(tof_0.distance / 10, 30)
label4.setText("Distance: {} mm".format(dis))
temp = env2_0.temperature
hum = env2_0.humidity
label5.setText("Temperature: {}°C".format(temp))
label6.setText("Humidity: {}%".format(hum))
if (temp > SAFE_TEMP or hum > SAFE_HUM or dis > 25) and not url_sent:
rgb.setColorAll(0xFF0000)
try:
url_params = '?FOOD={}&TEMP={}&HUM={}&ID={}'.format(dis, env2_0.temperature, env2_0.humidity, "NIL")
full_url = Google_URL + url_params
send_request(full_url)
url_sent = True
gc.collect()
except Exception as e:
label0.setText('Exception: {}'.format(e))
elif temp <= SAFE_TEMP and hum <= SAFE_HUM and dis <= 25:
url_sent = False
if 0 <= dis <= 10:
rgb.setColorAll(0x00FF00)
elif 10 <= dis <= 20:
rgb.setColorAll(0xFFFF00)
elif 20 <= dis <= 25:
rgb.setColorAll(0xFFA500)
else:
rgb.setColorAll(0x000000)
card_present = rfid_0.isCardOn()
label0.setText(str(card_present))
if card_present:
current_id = rfid_0.readUid()
label1.setText(str(current_id))
if id1 is None:
id1 = current_id
label2.setText(str(id1))
servo_1.write_angle(90)
id1_timer_start = time.ticks_ms()
elif current_id != id1 and current_id != id2:
if id2 is None:
id2 = current_id
label3.setText(str(id2))
servo_1.write_angle(0)
id2_timer_start = time.ticks_ms()
if current_id == id1:
if time.ticks_diff(time.ticks_ms(), id1_timer_start) > timer_duration * 1000:
servo_1.write_angle(90)
time.sleep(5)
id1_timer_start = time.ticks_ms()
else:
rgb.setColorAll(0x000000)
elif current_id == id2:
if time.ticks_diff(time.ticks_ms(), id2_timer_start) > timer_duration * 1000:
servo_1.write_angle(0)
time.sleep(5)
id2_timer_start = time.ticks_ms()
else:
rgb.setColorAll(0x000000)
Data_send = None
wifiCfg.wlan_ap.active(True)
wifiCfg.wlan_sta.active(True)
espnow.init()
espnow.send_cb(send_cb)
espnow.add_peer('98:f4:ab:6b:a2:65', id=1)
url_params = '?FOOD={}&TEMP={}&HUM={}&ID={}'.format(dis, env2_0.temperature, env2_0.humidity,current_id)
full_url = Google_URL + url_params
Data_send = 'Turn on Motor'
label0.setText(str(Data_send))
espnow.send(id=1, data=str('Turn on Motor'))
wait(3)
Data_send = 'Stop Motor'
espnow.send(id=1, data=str('Stop Motor'))
label0.setText(str(Data_send))
print('connecting to wifi')
wifiCfg.wlan_ap.active(False)
wifiCfg.wlan_sta.active(False)
wifi.active(True)
wifi.connect(WIFI_SSID, WIFI_PASSWORD)
while not wifi.isconnected():
pass
send_request(full_url)
print('Disconnecting from wifi')
wifi.disconnect()
wifi.active(False)
while wifi.isconnected():
pass
print('connecting to ESP-Now')
wifiCfg.wlan_ap.active(True)
wifiCfg.wlan_sta.active(True)
espnow.init()
gc.collect()
wait(0.5)
wait_ms(2)
function autofillgoogledocs() //creates menu on the sheet itself to mamually add
{
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('Test fill');
menu.addItem('Create New DATA', 'doGet')
menu.addToUi();
}
function doGet(e) { //function to actually get value from web app
Logger.log( JSON.stringify(e) );
var result = 'Ok';
if (e.parameter == 'undefined' ) {
result = 'No Parameters';
}
else {
var sheet_id = ' ';
var sheet = SpreadsheetApp.openById(sheet_id).getActiveSheet();
var newRow = sheet.getLastRow() + 1;
var rowData = [];
var Curr_Date = new Date();
rowData[0] = Curr_Date;
var Curr_Time = Utilities.formatDate(Curr_Date, "Asia/Singapore", 'HH:mm:ss'); //time and date put into row 0 and 1
rowData[1] = Curr_Time;
for (var param in e.parameter) {
Logger.log('In for loop, para=' + param);
var value = stripQuotes(e.parameter[param]);
Logger.log(param + ':' + e.parameter[param]);
switch (param) {
case 'FOOD': //add food value and put in row 2
rowData[2] = value;
result = 'Food level';
break;
case 'TEMP': //temp value put in row 3
rowData[3] = value;
result = 'TEMPERATURE';
break;
case 'HUM': //temp value put in row 3
rowData[4] = value;
result = 'HUMIDITY';
break;
case 'ID': //temp value put in row 3
rowData[5] = value;
result = 'doggo';
break;
default:
result = "unsupported parameter"
}
}
}
if(rowData[2] > 24 ) // sends notifcation function
{
var message = rowData[0]
var subject = 'food is low'
MailApp.sendEmail('samuelkoh5104@gmail.com', subject, message); // sends email
sendTweets(); // call tweet function
}
Logger.log(JSON.stringify(rowData));
var newRange = sheet.getRange(newRow, 1, 1, rowData.length);
newRange.setValues([rowData]);
return ContentService.createTextOutput(result);
}
function stripQuotes( value ) {
return value.replace(/^["']|['"]$/g, "");
}
function sendTweets() {
var sheet_id = '1uh5qxmDVFH3C_FXo91L19zQmVu77BH9fi56BIi4v_jk';
var sheet = SpreadsheetApp.openById(sheet_id).getActiveSheet();
var newRow = sheet.getLastRow() + 1;
var rowData = [];
var Curr_Date = new Date();
rowData[0] = Curr_Date;
var Curr_Time = Utilities.formatDate(Curr_Date, "Asia/Singapore", 'HH:mm:ss');
rowData[1] = Curr_Time;
var twitterKeys = {
TWITTER_CONSUMER_KEY: " ",
TWITTER_CONSUMER_SECRET: " ",
TWITTER_ACCESS_TOKEN: " ",
TWITTER_ACCESS_SECRET: " ",
}
var props = PropertiesService.getScriptProperties();
props.setProperties(twitterKeys);
var params = "rowData[1]";
var service = new Twitterlib.OAuth(props);
; if (!service.hasAccess()) {
console.log("Authentication Failed");
} else {
console.log("Authentication Successful");
var status = ("food level is low please refill feeder "+ rowData[0]); //tweet message
try {
var response = service.sendTweet(status, params);
console.log(response);
} catch (e) { console.log(e) }
}
}
from m5stack import *
from m5ui import *
from uiflow import *
import espnow
import wifiCfg
import time
import unit
setScreenColor(0x222222)
servo_6 = unit.get(unit.SERVO, unit.PORTA)
angle_0 = unit.get(unit.ANGLE, unit.PORTB)
mac = None
Data_send = None
weight = None
j = None
feed3 = None
angleunit = None
wifiCfg.wlan_ap.active(True)
wifiCfg.wlan_sta.active(True)
espnow.init()
label0 = M5TextBox(10, 10, "Text", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label1 = M5TextBox(10, 40, "Text", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label2 = M5TextBox(10, 70, "Text", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label3 = M5TextBox(10, 100, "Text", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label4 = M5TextBox(10, 130, "Text", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label5 = M5TextBox(10, 160, "Text", lcd.FONT_Default, 0xFFFFFF, rotate=0)
label6 = M5TextBox(10, 190, "Text", lcd.FONT_Default, 0xFFFFFF, rotate=0)
from numbers import Number
def upRange(start, stop, step):
while start <= stop:
yield start
start += abs(step)
def downRange(start, stop, step):
while start >= stop:
yield start
start -= abs(step)
def recv_cb(_):
global mac,Data_send,weight,feed3,angleunit
mac, _, Data_send = espnow.recv_data(encoder='str')
label1.setText(str(Data_send))
servo_6.write_angle(0)
if Data_send == 'on':
for j in (1 <= float(feed3)) and upRange(1, float(feed3), 1) or downRange(1, float(feed3), 1):
servo_6.write_angle(80)
wait(1)
servo_6.write_angle(0)
wait(1)
else:
servo_6.write_angle(0)
pass
espnow.recv_cb(recv_cb)
def buttonA_wasPressed():
global mac, Data_send, weight, feed3, angleunit
if weight < 5:
weight = (weight if isinstance(weight, Number) else 0) + 1
pass
btnA.wasPressed(buttonA_wasPressed)
def buttonB_wasPressed():
global mac, Data_send, weight, feed3, angleunit
if weight > 0:
weight = (weight if isinstance(weight, Number) else 0) + -1
pass
btnB.wasPressed(buttonB_wasPressed)
label0.setText(str(espnow.get_mac_addr()))
weight = 5
while True:
label5.setText(str(angle_0.read()))
angleunit = int(((angle_0.read()) / 200))
feed3 = int(angleunit) * weight
label0.setText(str(feed3))
label6.setText(str(weight))
wait_ms(2)
Comments