Vítor Barbosa
Published © CC BY-NC

Pet Center - Feed and Entertain Your Pet

A simple and effective Alexa-based system to provide food and fun to your dog or cat.

IntermediateFull instructions provided2 days7,798
Pet Center - Feed and Entertain Your Pet

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
ESP8266 ESP-01
Espressif ESP8266 ESP-01
×1
SG90 Micro-servo motor
SG90 Micro-servo motor
×2
Pan/Tilt Camera Module
×1
RobotGeek Relay
RobotGeek Relay
×1
5mW Red dot laser module
×1
3.3V Regulator
×1

Software apps and online services

Arduino IDE
Arduino IDE
Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
Google Compute Engine
Eclipse IoT Broker

Hand tools and fabrication machines

FT232RL USB-Serial Converter

Story

Read more

Schematics

Main circuit

View of the main circuit. The AC motor is the pet feeding motor

Code

ESP8266 MQTT to Serial Bridge

Arduino
Connects to remote MQTT server, subscribes to a topic and delivers every message to Arduino board through Serial port
//#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>

//Enable/disable debug messages
#define DEBUG false


// Update these with values suitable for your network.
const char* ssid = "SSID";
const char* password = "PASS";
const char* mqtt_server = "iot.eclipse.org";
const int mqtt_port = 1883;

//topic to subscribe
const char* inTopic = "petcenter"; 
//topic to publish
const char* outTopic = "petcenter";

//Serial baud rate
const int baud = 115200;

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  if (DEBUG) Serial.println();
  if (DEBUG) Serial.print("Connecting to ");
  if (DEBUG) Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    if (DEBUG) Serial.print(".");
  }

  if (DEBUG) Serial.println("");
  if (DEBUG) Serial.println("WiFi connected");
  if (DEBUG) Serial.println("IP address: ");
  if (DEBUG) Serial.println(WiFi.localIP());
}

//Will be called once a message is received
void callback(char* topic, byte* payload, unsigned int length) {
  if (DEBUG) Serial.print("Message arrived [");
  if (DEBUG) Serial.print(topic);
  if (DEBUG) Serial.print("] ");
  for (int i = 0; i < length; i++) {
     Serial.print((char)payload[i]);
  }
  Serial.println();

}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    if (DEBUG) Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client")) {
      if (DEBUG) Serial.println("connected");
      // Once connected, publish an announcement...
      client.publish(outTopic, "hello world");
      // ... and resubscribe
      client.subscribe(inTopic);
    } else {
      if (DEBUG) Serial.print("failed, rc=");
      if (DEBUG) Serial.print(client.state());
      if (DEBUG) Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup() {
  Serial.begin(baud);
  setup_wifi();
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
}

//Not used, will send hello world every 2 secs
void sendTestMsg(){
  long now = millis();
  if (now - lastMsg > 2000) {
    lastMsg = now;
    ++value;
    snprintf (msg, 75, "hello world #%ld", value);
    if (DEBUG) Serial.print("Publish message: ");
    if (DEBUG) Serial.println(msg);
    client.publish(outTopic, msg);
  }
}

void loop() {

  if (!client.connected()) {
    reconnect();
  }
  client.loop();

}

petcenter-arduino-nano.ino

Arduino
Arduino code for Arduino Nano. Controls the servo, laser diode and pet feeding relay
#include <Arduino.h>
#include <Servo.h>
#include <ArduinoJson.h>

Servo servoH;
Servo servoV;

StaticJsonBuffer<200> jsonBuffer;

const int laserPin = 7;
const int relayPin = 4; // relay is active low

//Servo limits
const int hLim[] = {70,150};
const int vLim[] = {20,70};

//Init servo pos to middle
int posH = (hLim[1] + hLim[0])/2;
int posV = (vLim[1] + vLim[0])/2;

//Food motor ON time 
const unsigned long FOOD_DURATION = 35000;

bool shouldServeFood = false;
unsigned long lastFoodTime = 0;


void goTo(int target,Servo* servo, int* current){
//Slowly go to target position
while(*current>target){
    *current-=5;
    if(*current<target) *current = target;
    servo->write(*current);
    delay(75);
}
while(*current<target){
    *current+=5;
    if(*current>target) *current = target;
    servo->write(*current);
    delay(75);
}

}

void randomMotion(){
  //Randomly move both servos
    digitalWrite(laserPin,HIGH);

    for(int i=0;i<6;i++){
        goTo(random(hLim[0],hLim[1]),&servoH,&posH);
        delay(1000);
        goTo(random(vLim[0],vLim[1]),&servoV,&posV);
        delay(1000);
    }
    digitalWrite(laserPin,LOW);
}



void setup() {
    Serial.begin(115200);

    servoH.attach(9);
    servoV.attach(10);

    pinMode(laserPin,OUTPUT);
    pinMode(relayPin,OUTPUT);

    digitalWrite(laserPin,LOW);
    digitalWrite(relayPin,HIGH);

    servoH.write(posH);
    servoV.write(posV);

}

void manageFood(){
  //Keep food motor ON for the specified time, then turn it OFF
  
    if(millis()-lastFoodTime>FOOD_DURATION)
        shouldServeFood = false;
    
    if(shouldServeFood)
        digitalWrite(relayPin,LOW);
    else
        digitalWrite(relayPin,HIGH);
}

void loop() {

    manageFood();
    
    if(Serial.available()<4)
        return;

    JsonObject& root = jsonBuffer.parse(Serial);
    String cmd = root["cmd"];

    if(cmd=="feed"){
        shouldServeFood = true;
        lastFoodTime = millis();
    }
    else if(cmd=="play"){
        randomMotion();
    }
    else if(cmd=="blink"){
        digitalWrite(laserPin,HIGH);
        delay(1000);
        digitalWrite(laserPin,LOW);
    }

}

petcenter.py

Python
Python 3 code for our endpoint, you will need either ngrok or localtunnel to make it publicy available to Alexa.
Don't forget to put the templates.yaml file in the same folder as this.
from flask import Flask, render_template
from flask_ask import Ask, statement, question, session 
import requests
import json
import paho.mqtt.client as mqtt 

app = Flask(__name__)
ask = Ask(app,'/')

mqtt_broker_host = 'iot.eclipse.org' #"127.0.0.1"
mqtt_broker_port = 1883
mqtt_keepalive_secs = 60

mqtt_default_topic = 'petcenter'

# The callback for when the client receives a CONNACK response from the mqtt server. 
def on_connect(client, userdata, flags, rc): 
	print("Connected with result code "+str(rc)+'\n') 

def quick_pub(msg,topic):
	#client = mqtt.Client(transport="websockets") 
	client = mqtt.Client()
	client.on_connect = on_connect 
	client.connect(mqtt_broker_host, mqtt_broker_port, mqtt_keepalive_secs) 
	client.publish(topic,msg,qos=0,retain=False) 
	client.disconnect() 

@ask.launch 
def petcenter_launch(): 
   welcome_msg = render_template("welcome") 
   return question(welcome_msg).reprompt(welcome_msg)


@ask.intent("feed") 
def petcenter_feed(pet_type):
	if pet_type == None:
		pet_type = "pet"
	print("Feeding a : " + str(pet_type) +' right now \n')
	quick_pub('{\'cmd\':\'feed\'}',mqtt_default_topic) 
	msg = render_template("feed_response") 
	return statement(msg) 
    

@ask.intent("play")
def petcenter_play(pet_type):
	if pet_type == None:
		pet_type = "pet"
	print("Entertaining a : " + str(pet_type) +" right now \n")
	quick_pub('{\'cmd\':\'play\'}',mqtt_default_topic)
	msg = render_template("play_response", pet_type = pet_type)
	return statement(msg) 


@ask.intent('AMAZON.HelpIntent')
def help():
    help_text = render_template('help')
    return question(help_text).reprompt(help_text)


@ask.intent('AMAZON.StopIntent')
def stop():
    bye_text = render_template('bye')
    return statement(bye_text)


@ask.intent('AMAZON.CancelIntent')
def cancel():
    bye_text = render_template('bye')
    return statement(bye_text)


@ask.session_ended
def session_ended():
    return "{}", 200


if __name__ == "__main__": 
    app.run(debug=True) #default is localhost, port is 5000

templates.yaml

YAML
Response templates for Alexa skill endpoint. Put this in the same folder as petcenter.py, and don't rename it.
welcome: Welcome to Pet Center ! What would you like to do ?
feed_response: Doing it right now !
play_response: I'll give your {{pet_type}} a good time !
bye: Goodbye
help: You can ask pet center feed my dog, or pet center play with my kitty or, you can say exit. What can I help you with?

Credits

Vítor Barbosa

Vítor Barbosa

1 project • 1 follower

Comments