Page 1 of 1

Auto Cat Feeder

Posted: Sun Aug 12, 2018 3:46 pm
by amabitxs
I made a cat feeder. The old one started running constantly so I replaced the main board with an Arduino Duemilanove. Any feedback would be appreciated. I'll add another picture once I get it all put back together.
catFeeder.jpg
Code: Select all
//#define DEBUG_ON_SERIAL
//#define FEED_NOW

#include <Wire.h>
#include <SparkFunDS1307RTC.h>
#include "Arduino.h"
/*
Pin out: Duemilanove
A4 - SDA
A5 - SCL

2 - Rd Feed Sensor 
3 - Wr Motor Run Switch
4 - Rd Button for extra/immediate food
*/

// Const's
const int SENSOR_PIN = 2; // feed sensor
const int BUTTON_PIN = 3; // button to manual feed
const int MOTOR_PIN = 4; // NPN transistor to run feeder motor
const int LED_PIN = 13; // Onboard LED

// Variables
int timeTillNextFeeding = 719; // old diff
int timeFromLastFeeding = 50; 
int motorRunning = 0;
int feedAmount = 6;

class myTime
{
  /*
  To easily compare two times
  */

  public:

    myTime();
    myTime(int, int, int);
    myTime(uint8_t, uint8_t, uint8_t);

    bool operator<(const myTime &rhs) const 
    { 
      if (this->getH() < rhs.getH())
        return true;
      else if (this->getH() == rhs.getH() && this->getM() < rhs.getM())
        return true;
      else if (this->getH() == rhs.getH() && this->getM() == rhs.getM() && this->getS() < rhs.getS())
        return true;

      return false; 
    };
    bool operator==(const myTime &rhs) const 
    {
      if (this->getH() == rhs.getH() && this->getM() == rhs.getM() && this->getS() == rhs.getS())
        return true;
      return false;
    };
/*    bool operator>(const myTime &rhs) const 
    { 
      if (this < rhs || this == rhs)
        return false;
      return true;
    };
*/
/*    void printVars2(myTime &rhs)
    {
      Serial.print("-->");

      Serial.print(String(this->getH()) + ":");
      if (this->getM() < 10)
        Serial.print('0'); // Print leading '0' for minute
      Serial.print(String(this->getM())); // Print minute

      Serial.print(" | ");
      Serial.print(String(rhs.getH()) + ":");
      if (rhs.getM() < 10)
        Serial.print('0'); // Print leading '0' for minute
      Serial.println(String(rhs.getM())); // Print minute
    };*/
    int timeDiffT(myTime &rhs)
    {
    // returns the minutes diff between this time and passed time
      int first, secnd;
      first = (this->getH() * 60) + this->getM();
      secnd = (rhs.getH() * 60) + rhs.getM();

      timeTillNextFeeding = secnd - first;
      if (timeTillNextFeeding > 720)
        first = first + 1440;  
      if (timeTillNextFeeding < -720)
        secnd = secnd + 1440;
      timeTillNextFeeding = first - secnd;

      delay(500);
      return timeTillNextFeeding;
    };


    void setH(int h) { H = h; };
    void setM(int m) { M = m; };
    void setS(int s) { S = s; };
    void setMyTime(uint8_t h, uint8_t m, uint8_t s) { setMyTime(int(h), int(m), int(s)); };
    void setMyTime(int h, int m, int s)
    {
      setH(h);
      setM(m);
      setS(s);
    };
    
    int getH() { return H;};
    int getM() { return M;};
    int getS() { return S;};

  private:

    int H;
    int M;
    int S;
};

void pulseLED();

myTime::myTime() { setMyTime(0, 0, 0); }
myTime::myTime(int h, int m, int s) { setMyTime(h, m, s); }
myTime::myTime(uint8_t h, uint8_t m, uint8_t s) { setMyTime(h, m, s); }

myTime feedTime;
myTime currTime;
myTime lastFeedTime;

int timeDiff(myTime &lhs, myTime &rhs)
{
  // returns the minutes diff between two passed times
  int first, secnd, diff;
  first = (lhs.getH() * 60) + lhs.getM();
  secnd = (rhs.getH() * 60) + rhs.getM();

  diff = secnd - first;
  if (diff > 720)
    first = first + 1440;  
  if (diff < -720)
    secnd = secnd + 1440;
  diff = first - secnd;

  //delay(500);
  return diff;
}
bool timeToFeed()
{
  timeFromLastFeeding = timeDiff(lastFeedTime, currTime);
  timeTillNextFeeding = timeDiff(feedTime, currTime);

#ifdef DEBUG_ON_SERIAL
  Serial.print("timeToFeed --> L: - ");
  Serial.print(timeFromLastFeeding);
  Serial.print(" -> N: - ");
  Serial.println(timeTillNextFeeding);
#endif

  if (timeFromLastFeeding < 5)
    return false;
  if (timeTillNextFeeding < 5 && timeTillNextFeeding > -5)
    return true;
  return false;
}

void feedTheCat()
{
#ifdef DEBUG_ON_SERIAL
  Serial.print("feedTheCat --> ");
  Serial.println(motorRunning);
#endif

  if (timeToFeed())
  {
    motorRunning = feedAmount;
    digitalWrite(MOTOR_PIN, HIGH);
  }
  while (motorRunning > 0)
  {
    delay(2000);
    //--motorRunning; // backup in case the interupt isn't working
  }
  digitalWrite(MOTOR_PIN, LOW);
}

void setup() 
{
#ifdef DEBUG_ON_SERIAL
  Serial.begin(9600);
  while (!Serial) { ; } // wait for serial port to connect. Needed for native USB port only
#endif
  // initialize input / output pins:
  pinMode(LED_PIN, INPUT);
  digitalWrite(LED_PIN,HIGH);

  pinMode(MOTOR_PIN, OUTPUT);

  //digitalWrite(SENSOR_PIN,HIGH);
  pinMode(SENSOR_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(SENSOR_PIN), increment, FALLING);
  
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), interuptFeedNow, FALLING);


  rtc.begin(); // starts clock
  //rtc.autoTime(); // should never need to be run again
  //rtc.setTime(s, m, h, day, date, month, year)
  //rtc.setTime(0, 11, 23, 7, 11, 8, 18);

  
  feedTime.setMyTime(7,30,0); // hrs, min, sec
}

void loop() 
{
  int i = 0;
  rtc.update(); // Update RTC data
  currTime.setMyTime(rtc.hour(), rtc.minute(), rtc.second());

#ifdef DEBUG_ON_SERIAL
#ifdef FEED_NOW
  feedTime = currTime;
#endif
#endif
  

  if (timeToFeed())
  {
#ifdef DEBUG_ON_SERIAL
    Serial.println("**** FEEDING ****");
    delay(1000);
#endif
    feedTheCat();
  }
#ifdef DEBUG_ON_SERIAL  
  printTime();
  printVars();
#endif

  //    pulseLED();
  if (timeTillNextFeeding < 0 && timeFromLastFeeding > 0)
  {
    //sleep for 60
#ifdef DEBUG_ON_SERIAL
    Serial.println("**** SLEEPING for 60 mins ****");
    delay(2000);
#else
  // what do do in real time
#endif
  }
  else if (timeTillNextFeeding > 30)
  {
    //sleep for 30
#ifdef DEBUG_ON_SERIAL
    Serial.println("**** SLEEPING for 30 mins ****");
    delay(2000);
#else
  // what do do in real time
#endif
  }
  else if (timeTillNextFeeding <= 30 && timeTillNextFeeding > 5)
  {
    //sleep for 3
#ifdef DEBUG_ON_SERIAL
    Serial.println("**** SLEEPING for 3 mins ****");
    delay(2000);
#else
  // what do do in real time
#endif
  }
  else if (timeTillNextFeeding <= 5)
  {
    //sleep for 3
#ifdef DEBUG_ON_SERIAL
    Serial.println("**** SLEEPING for 15 s ****");
    delay(1000);
#else
  // what do do in real time
#endif
  }
}
void increment()  // Really a decrement... shuts down the motor when the counter gets to less than zero
{
#ifdef DEBUG_ON_SERIAL
  Serial.print("**** Interupt 1 **** Cups left to feed: ");
#endif
  delay(50);

  --motorRunning;
  if (motorRunning < 0)
    digitalWrite(MOTOR_PIN, LOW);

#ifdef DEBUG_ON_SERIAL
  Serial.println(motorRunning);
#endif
}
void interuptFeedNow()  // Really a decrement... shuts down the motor when the counter gets to less than zero
{
#ifdef DEBUG_ON_SERIAL
  Serial.print("**** Interupt 2 ****");
#endif
  delay(50);
  digitalWrite(MOTOR_PIN, HIGH);
  motorRunning = feedAmount;
  increment();

#ifdef DEBUG_ON_SERIAL
  Serial.println();
#endif
}
#ifdef DEBUG_ON_SERIAL
void printTime()
{

  Serial.print(String(rtc.hour()) + ":"); // Print hour
  if (rtc.minute() < 10)
    Serial.print('0'); // Print leading '0' for minute
  Serial.print(String(rtc.minute()) + ":"); // Print minute
  if (rtc.second() < 10)
    Serial.print('0'); // Print leading '0' for second
  Serial.print(String(rtc.second())); // Print second

  if (rtc.is12Hour()) // If we're in 12-hour mode
  {
    // Use rtc.pm() to read the AM/PM state of the hour
    if (rtc.pm()) Serial.print(" PM"); // Returns true if PM
    else Serial.print(" AM");
  }

  Serial.print(" | ");

  // Few options for printing the day, pick one:
  Serial.print(rtc.dayStr()); // Print day string
  //Serial.print(rtc.dayC()); // Print day character
  //Serial.print(rtc.day()); // Print day integer (1-7, Sun-Sat)
  Serial.print(" - ");
  Serial.print(String(rtc.month()) + "/" +   // Print month
  String(rtc.date()) + "/");  // Print date
  Serial.print(String(rtc.year()));        // Print year

  Serial.print(" .. ");
  Serial.print(timeTillNextFeeding);
  Serial.println();

}
void printVars()
{
  Serial.print("-> ");

  Serial.print(String(feedTime.getH()) + ":");
  if (feedTime.getM() < 10)
    Serial.print('0'); // Print leading '0' for minute
  Serial.print(String(feedTime.getM())); // Print minute

  Serial.print(" | ");
  Serial.print(String(currTime.getH()) + ":");
  if (currTime.getM() < 10)
    Serial.print('0'); // Print leading '0' for minute
  Serial.println(String(currTime.getM())); // Print minute
}
#endif

Re: Auto Cat Feeder

Posted: Sat Sep 15, 2018 7:55 am
by Valen
1: You lack a diode across the motor terminals. Once the transistor shuts off the energized coil causes a voltage spike that could damage the transistor. Connect the diode such that the cathode is on the positive motor terminal. When the transistor turns off the current can flow back to the positive motor terminal and disipate the magnetic field. The led won't solve this as it is the other way around.

2: Fritzing diagrams do not show the proper pin orientation. It doesn't show emitter, base and collector orientation for transistors. Same goes for the switches. How could we tell the conductive path between the 4 terminals. A normal electrical symbol schematic would (if done properly) So for all we know your circuit could fail to work because of wrong transistor and/or switch orientation.

Codewise: It could use some clean up jobs! Seems like there are a bunch of code-leftovers. Which doesn't entice me to look too deep into it. Too confusing

Comparing time in that myTime class. (specifically the operator definition) Isn't it easier to just multiply hours by 60 and add minutes to get the minute-of-the-day (ignoring seconds for simplicity but adding that level of precision isn't that hard) and compare those? Your solution seems rather convoluted with if-statements.

You do seem to do that already in the function timeDiffT. Hmm, above it you define the < and = operator for the class but comment out the > operator. Strange business.

In setup LED_PIN is configured set as input and subsequently written high! Doesn't hurt, but strange also.

You introduce several delay statements (multiple seconds) in code parts that are only compiled in when a compiler directive is set "DEBUG_ON_SERIAL". Doesn't that cause irregular timing behavior? Why wait at all?

Probably more could be said about it but this is where I leave it.