Table of Contents

Cat Feeder

  • Nume: Mitu Cristina-Stefania
  • Grupa: 331CB

Introducere

Proiectul meu îmbină tehnologia modernă cu nevoile zilnice ale stăpânilor de pisici, oferind o soluție simplă și eficientă pentru hrănirea lor: un dispenser automat de hrănire. Cu doar o apăsare de buton și setând ora potrivită, stăpânii de pisici pot asigura hrana la timp pentru prietenii lor pufoși.

Descriere generală

Conectând un cablu USB de la laptop la placa Arduino Uno, aceasta devine centrul de control al întregului sistem. Pe display este afișat numele proiectului pe prima linie, iar ora și data curentă pe a doua și a treia linie. La apăsarea butonului dedicat, se declanșează o întrerupere care va afișa un meniu pe ecran. Utilizatorul are trei opțiuni principale: apăsând tasta 'A' poate introduce manual ora la care se va elibera hrana, sau apăsând tasta 'B' (meniu dietă) sau 'C' (meniu normal), poate seta hrănirea automată a pisicii de trei ori pe zi la ore prestabilite.

Dacă un meniu a fost deja ales și utilizatorul dorește să selecteze un alt meniu, poate apăsa tasta 'D' pentru a reseta meniul, iar pe ecran va apărea mesajul 'Meniul a fost șters!'. Dacă se încearcă introducerea unei alte taste după ce un meniu a fost selectat, pe ecran va apărea mesajul 'Ai introdus deja un meniu!'. În cazul în care nu a fost ales niciun meniu și se introduce o altă tastă decât cele specificate, se va afișa mesajul 'Tastă invalidă'. De asemenea, dacă utilizatorul a introdus greșit ora după selectarea tastei 'A', poate apăsa tasta 'D' pentru a anula ora.

Motorul servo, controlat de Arduino, este esențial în acest sistem. După ce utilizatorul a ales opțiunea dorită, dispozitivul așteaptă până când modulul RTC indică că ora presetată a fost atinsă. În acel moment, Arduino activează motorul servo, care acționează ușița pentru a elibera hrana pentru pisică. Această integrare eficientă a modulelor RTC și servo motor asigură o hrănire precisă și la timp pentru animalul de companie.

Hardware Design

Conexiuni

Tastatura are 8 pini care se conectează la pinurile digitale ale Arduino pentru a detecta apăsările de taste.

  • Pin 1 la pinul digital 2 al Arduino
  • Pin 2 la pinul digital 3 al Arduino
  • Pin 3 la pinul digital 4 al Arduino
  • Pin 4 la pinul digital 5 al Arduino
  • Pin 5 la pinul digital 6 al Arduino
  • Pin 6 la pinul digital 7 al Arduino
  • Pin 7 la pinul digital 8 al Arduino
  • Pin 8 la pinul digital 9 al Arduino

Modulul RTC are 4 pini: VCC, GND, SDA, SCL, și utilizează comunicarea I2C pentru sincronizarea timpului.

  • VCC la 5V al Arduino
  • GND la GND al Arduino
  • SDA la pinul analogic A4 al Arduino
  • SCL la pinul analogic A5 al Arduino

Display-ul LCD simplifică conexiunile datorită modulului I2C integrat, reducând necesitatea de pini suplimentari.

  • VCC la 5V al Arduino
  • GND la GND al Arduino
  • SDA la (I2C) SDA al Arduino
  • SCL la (I2C) SCL al Arduino

Butonul tactil detectează apăsările pentru a activa meniul de setări.

  • Un pin la pinul analogic A3 al Arduino
  • Celălalt pin la GND (prin intermediul unei rezistențe de 4.7kΩ)
  • Alt pin la 5V al Arduino

Servo motorul are 3 pini: VCC, GND și Semnal, și controlează mecanismul de eliberare a hranei.

  • VCC la 5V al Arduino
  • GND la GND al Arduino
  • Semnal la pinul digital 10 al Arduino

Software Design

Mediul de dezvoltare utilizat pentru realizarea proiectului a fost: https://www.arduino.cc/en/software/ - versiunea 2.3.2

Alegerea bibliotecilor:

  • Keypad.h: pentru interacțiunea cu tastatura.
  • Wire.h: pentru comunicarea I2C.
  • LiquidCrystal_I2C.h: pentru controlul display-ului LCD prin I2C.
  • RTClib.h: pentru interacțiunea cu modulul RTC (DS3231).
  • Servo.h: pentru controlul motorului servo.

Element de noutate al proiectului

Elementul de noutate al proiectului constă în integrarea completă a mai multor componente hardware și software pentru a crea un sistem autonom de hrănire a pisicilor. Utilizarea unui meniu interactiv pentru setarea orelor de hrănire și posibilitatea de a selecta diferite programe de hrănire sunt caracteristici inovative care oferă flexibilitate și ușurință în utilizare.

Descrierea codului:

Servo servo1;
int servoPin = 10;
 
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = { 2, 3, 4, 5 };
byte colPins[COLS] = { 6, 7, 8, 9 };
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
 
LiquidCrystal_I2C lcd(0x27, 20, 4);
RTC_DS3231 rtc;
 
const int buttonPin = A3; // Pinul la care este conectat butonul
int buttonState = 0; // Variabila pentru a stoca starea butonului (apasat sau nu)
bool showMenu = false; // Dacă este true se afișează meniul
boolean feed = false; // Dacă a fost ales meniul A true, altfel false
int feedingHour = -1;
int feedingMinute = -1;
int menuSelected = 0;  // 0 = Niciun meniu, 1 = Meniu B dieta, 2 = Meniu C normal, 3 = Meniu A
int presetFeedingTimes[3][3]; // Ora, minut, secundă  pentru hrănirile prestabilite
bool butB = false; // Dacă a fost ales meniul B true, altfel false
bool butC = false; // Dacă a fost ales meniul C true, altfel false
void setup() {
  Wire.begin();
  lcd.begin(20, 4);
  lcd.backlight();
  servo1.attach(servoPin);
 
  presetFeedingTimes[0][0] = 20; // Prima ora de hrănire prestabilită
  presetFeedingTimes[0][1] = 44; // Minutul
  presetFeedingTimes[0][2] = 0;  // Secunda
  presetFeedingTimes[1][0] = 20; // A doua ora de hrănire prestabilită
  presetFeedingTimes[1][1] = 44;
  presetFeedingTimes[1][2] = 20;
  presetFeedingTimes[2][0] = 20; // A treia ora de hrănire prestabilită
  presetFeedingTimes[2][1] = 44;
  presetFeedingTimes[2][2] = 35;
 
  if (!rtc.begin()) {
    lcd.print("Couldn't find RTC");
    while (1);
  }
 
  if (rtc.lostPower()) {
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
 
  pinMode(buttonPin, INPUT); // Seteaza pinul butonului ca pin de intrare
}
void loop() {
  // Citeste starea butonului
  int buttonPress = digitalRead(buttonPin);
 
  // Dacă butonul este apăsat
  if (buttonPress == 1) {
    showMenu = true;
    lcd.clear(); // Curăță ecranul pentru a afișa meniul
    lcd.setCursor(0, 0);
    lcd.print("Selecteaza tasta: ");
    lcd.setCursor(0, 1);
    lcd.print("A - Alege ora mesei");
    lcd.setCursor(0, 2);
    lcd.print("B - Meniu 1 dieta");
    lcd.setCursor(0, 3);
    lcd.print("C - Meniu 2 normal");
    delay(500); // Debounce delay
  } else if (showMenu) {
    // Citeste tasta apasata de la keypad
    char key = keypad.getKey();
 
    if (key) {
      lcd.clear();
      switch (key) {
        case 'A':
          if (menuSelected == 0) {
            lcd.setCursor(0, 0);
            lcd.print("Ai ales tasta 'A'");
            setFeedingTimeA();
            menuSelected = 3;
          } else {
            lcd.setCursor(0, 0);
            lcd.print("Ai introdus deja un meniu!");
          }
          break;
        case 'B':
          if (menuSelected == 0) {
            lcd.setCursor(0, 0);
            lcd.print("Ai ales tasta 'B'");
            menuSelected = 1;
            butB = true;
          } else {
            lcd.setCursor(0, 0);
            lcd.print("Ai introdus deja un meniu!");
          }
          break;
        case 'C':
          if (menuSelected == 0) {
            lcd.setCursor(0, 0);
            lcd.print("Ai ales tasta 'C'");
            menuSelected = 2;
            butC = true;
          } else {
            lcd.setCursor(0, 0);
            lcd.print("Ai introdus deja un meniu!");
          }
          break;
        case 'D':
          if (menuSelected == 0) {
            lcd.setCursor(0, 0);
            lcd.print("Tasta invalida");
          } else {
            lcd.setCursor(0, 0);
            lcd.print("Meniul a fost sters!");
            menuSelected = 0;
            butB = false;
            butC = false;
            feed = false;
          }
          break;
        default:
          lcd.setCursor(0, 0);
          lcd.print("Tasta invalida");
          break;
      }
      delay(2000); // Afișează mesajul timp de 2 secunde
      showMenu = false; // Revine la afișarea datei și orei după selectarea opțiunii
    }
  } else {
    // Afiseaza data si ora curenta pe ecran
    lcd.clear();
    DateTime now = rtc.now();
    lcd.setCursor(0, 0);
    lcd.print("CAT FEEDER! <3");
    lcd.setCursor(0, 1);
    if (now.hour() < 10) lcd.print('0');
    lcd.print(now.hour());
    lcd.print(':');
    if (now.minute() < 10) lcd.print('0');
    lcd.print(now.minute());
    lcd.print(':');
    if (now.second() < 10) lcd.print('0');
    lcd.print(now.second());
 
    lcd.setCursor(0, 2);
    lcd.print("Date: ");
    if (now.day() < 10) lcd.print('0');
    lcd.print(now.day());
    lcd.print('/');
    if (now.month() < 10) lcd.print('0');
    lcd.print(now.month());
    lcd.print('/');
    lcd.print(now.year());
 
    // Verifică dacă este ora de hrănire setată pentru meniul A
    if (now.hour() == feedingHour && now.minute() == feedingMinute && feed) {
      servo1.write(-90);
      delay(1000);
      servo1.write(90);
      delay(1000);
      menuSelected = 0;
      feed = false; // Dezactivează hrănirea după efectuare
    }
    // Dacă a fost ales meniul B
    if (butB == true) {
      int i;
      for (i = 0; i < 3; i ++) {
        if (now.hour() == presetFeedingTimes[i][0] && now.minute() == presetFeedingTimes[i][1] && now.second() == presetFeedingTimes[i][2]) {
          servo1.write(-90);
          delay(1000);
          servo1.write(90);
          delay(2000);
          if (i == 2) {
            butB = false;
            menuSelected = 0;
          }
          break;
        }
      }
    }
    // Dacă a fost ales meniul C
    if (butC == true) {
      int i;
      for (i = 0; i < 3; i ++) {
        if (now.hour() == presetFeedingTimes[i][0] && now.minute() == presetFeedingTimes[i][1] && now.second() == presetFeedingTimes[i][2]) {
          servo1.write(-90);
          delay(1000);
          servo1.write(90);
          delay(2000);
          servo1.write(-90);
          delay(1000);
          servo1.write(90);
          delay(2000);
          if (i == 2) {
            butC = false;
            menuSelected = 0;
          }
          break;
        }
      }
    }
  }
 
  delay(100); // Mică întârziere pentru debounce
}
void setFeedingTimeA()
{
  feed = true;
   int i = 0;
   int j = 0;
   char key;
   char r[4];
 
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set feeding Time");
  lcd.setCursor(0, 1);
  lcd.print("HH:MM");
  lcd.setCursor(0, 2);
  while(1){
    key = keypad.getKey();
    if(key >= '0' && key <= '9'){ // Acceptă doar cifre
      lcd.setCursor(j, 2);
      lcd.print(key);
      r[i] = key - '0'; // Convertește tasta apăsată la valoare numerică
      i++;
      j++;
 
      if (j == 2)
      {
        lcd.print(":");
        j++;
      }
 
      if (i == 4) { // Toate cifrele au fost introduse
        feedingHour = r[0] * 10 + r[1];
        feedingMinute = r[2] * 10 + r[3];
        break;
      }
    }
 
    if (key == 'D') { // Anulează setarea
      feed = false;
      break;
    }
  }
}

Funcționalități din laborator

Proiectul integrează mai multe noțiuni fundamentale învățate în laboratoare, cum ar fi utilizarea ADC, I2C, Keypad, RTC și Servo.

Rezultate Obţinute

Cu toate că Oscar, motanul meu, nu pare să fie impresionat de proiect, știu că are o latură entuziasmată pe care o păstrează pentru el.:-D

Rezumat video:

Inițial, am selectat tasta 'C' pentru a seta meniul normal, cu orele prestabilite la 16:04:00, 16:04:20 și 16:04:35. În timp ce așteptam să se facă orele respective, am vrut să testez și alte funcționalități ale sistemului. Pentru a verifica dacă ecranul afișează mesajele corecte, am selectat un alt meniu la întâmplare. Am vrut să mă asigur că mesajul “Ai introdus deja un meniu!” apare pe ecran atunci când se încearcă setarea unui meniu nou după ce unul este deja activ. Exact așa s-a întâmplat: ecranul a afișat corect mesajul, indicând că nu pot seta un alt meniu până nu îl șterg pe cel actual. Apoi, am ales o tasta la întâmplare, alta decât cele specifice meniului, pentru a testa cum se comportă sistemul. După apăsarea tastei necorespunzătoare, ecranul a afișat mesajul “Tasta invalida!”, confirmând că programul recunoaște și gestionează corect inputurile incorecte. La ora 16:04:00, sistemul a început să execute meniul 'C'. Conform specificațiilor, meniul 'C' face două ture pentru fiecare oră prestabilită, rotind servomecanismul cu -90 de grade și apoi înapoi la 90 de grade. Această secvență s-a repetat și la 16:04:20 și 16:04:35, confirmând că funcția a fost implementată corect. După ce aceste ore au trecut și meniul 'C' și-a încheiat ciclul, am introdus meniul 'A'. Pentru acesta, a trebuit să aleg ora de hrănire manual, introducând-o prin keypad. Am setat ora la 16:05, iar sistemul a acceptat și afișat noua oră corect. Ulterior, am ales meniul 'B' și am vrut să testez funcția de ștergere a meniului. Am apăsat tasta 'D', iar ecranul a afișat mesajul “Meniul a fost sters!”, indicând că toate meniurile active au fost resetate și sistemul este pregătit pentru o nouă selecție.

Concluzii

Realizarea acestui proiect a fost o experiență extrem de plăcută și interesantă pentru mine. Am avut ocazia să îmbin dragostea mea pentru pisici cu cerințele unei teme academice, ceea ce a dus la crearea unui dispozitiv util și ingenios. Procesul de dezvoltare a fost captivant, deoarece am avut posibilitatea să aplic cunoștințele dobândite la laboratoarele de PM într-un mod practic și relevant. Acest proiect nu este doar o temă de facultate; este un dispozitiv pe care îl voi folosi pentru a-mi hrăni pisica. Faptul că am reușit să creez ceva atât de util și de drag mie îmi aduce o mare bucurie. Pe parcursul realizării acestui proiect, m-am confruntat cu câteva probleme legate de designul ușiței dispozitivului. Inițial, am conceput ușița ca fiind un cerc cu un alt cerc decupat în el, prin care mâncarea să iasă. Cu toate acestea, în timpul testelor, am observat că orezul se bloca între decupatură și peretele pâlniei, ceea ce nu era acceptabil. Am testat diferite tipuri de mâncare, precum alune, cereale, orez, ovăz, zahăr și cafea măcinată. Ușița inițială funcționa fără blocări doar cu cafea măcinată, dar nu era textura ideală a mâncării pentru hrănirea pisicii. Pentru a rezolva această problemă, am modificat designul ușiței, transformând-o într-un semicerc. După această modificare, dispozitivul a funcționat fără blocări indiferent de tipul de mâncare utilizat. Această ajustare a fost esențială pentru a asigura funcționarea fiabilă a dispozitivului și pentru a mă asigura că pisica mea primește hrana necesară fără întreruperi.

Download

Jurnal

  • 1 mai: Achiziționare piese
  • 8 mai: Au ajuns piesele
  • 12 mai: Milestone - Hardware
  • 19 mai: Realizarea codului
  • 24 mai: Milestone - Software

Bibliografie/Resurse