Table of Contents

Automatic Cat Toy 🐱

Nume: Popescu Ioana-Denisa

Grupa: 336CA

Introducere

Considerate animale de companie crepusculare, majoritatea pisicilor domestice își manifestă activitatea noaptea, având un nivel ridicat de energie și dorință de joacă. Din păcate, factori precum programul încărcat și dorința naturală de odihnă îi împiedică pe proprietari să interacționeze cu felinele, care tânjesc, conform studiilor, după atenție umană.

Proiectul Automatic Cat Toy își propune să rezolve această problemă, venind ca o punte de mijloc care să împace ambele părți: felina se joacă fericită, iar proprietarul are o odihnă liniștită.

Descriere generală

Schema Bloc

Proiectul constă în implementarea unei jucării automate pentru pisici, componenta principală pe care se bazează fiind Arduino UNO ATmega328P. Vor exista, în principal, 2 moduri de funcționare, care vor putea fi selectate prin intermediul unui buton:

Hardware Design

Listă de piese

  • Arduino UNO - ATmega328P
  • Ecran LCD
  • Modul interfață I2C LCD
  • ServoMotor SG90
  • Senzor Ultrasonic HC-SR04
  • Potențiometru
  • 3 Led-uri
  • 3 rezistențe de 150 Ω
  • 1 rezistență de 1K Ω
  • 1 buton
  • Legătură cu un calculator pentru interfața serială
  • Fire, 2 Breadboard-uri
  • Condensator 2.2 nF

Schemă Hardware

Conectare componente

Modul interfață I2C LCD

Folosit atât pentru a reduce numărul pinilor necesari conectării Arduino, cât și pentru manevrarea cu ușurință a contrastului de pe LCD cu ajutorul potențiometrului incorporat.

  • GND → GND
  • VCC → 5V
  • SDA (Serial Data Pin) → A4
  • SCL (Clock Pin) → A5

ServoMotor SG90

Necesar mișcării jucăriei, atât la o poziție predefinită, cât și întâmplătoare, folosind PWM.

  • GND → GND
  • VCC → 5V
  • PWM → PD5

Senzor Ultrasonic HC-SR04

Măsoară distanța până la obiectul detectat, înmulțind timpul dintre transmiterea semnalului sender si primirea celui reciever (reflectat) cu viteza luminii.

  • GND → GND
  • VCC → 5V
  • Trig (trimite pulsul) → PD9
  • Echo (identifică reflexia de la obiect) → PD10

Potențiometru

Utilizat pentru a mișca servomotorul cu un anumit unghi, prin control variabil asupra semnalelor. Aici s-a folosit un convertor analog-digital, iar pinii de GND și Wiper au fost puși ulterior în paralel cu un condensator de 2.2 nF pentru stabilitate (valorile se aflau inițial într-o fluctuație continuă și era dificil de aproximat un rezultat).

În funcție de valoarea sa, se aprind ledurile succesiv: cel roșu pentru valori mai mari de 500, altfel, cel verde.

  • T1 → GND
  • Wiper → A0
  • T2 → VCC

Buton

La apăsare, se schimbă modurile de funcționare și se aprinde ledul galben. Determinarea stării butonului se face cu o întrerupere de tip Pin Change Interrupt.

  • Terminal 1b → D7
  • Terminal 2b → VCC

Circuit Fizic (variantă inițială)

Rezultatele simulării

În această etapă am conectat componentele Hardware și am scris si o parte din cod, bazându-mă strict pe funcții Arduino. Rezultatul a fost unul așteptat, funcționalitățile de bază mergeau, însă logica programului nu era încă implementată și optimizată, iar designul era încă la nivel teoretic. Ulterior, circuitul a fost puțin modificat deoarece am constatat că pinii pentru servomotor nu erau potriviți, am adăugat un condensator și am schimbat puțin poziționarea pentru a elimina firele redundante.

Software Design

În ceea ce privește partea de software, am folosit Arduino IDE cu următoarele biblioteci:

  • Servo.h pentru controlul servomotor-ului
  • LiquidCrystal_I2C.h pentru LCD
  • Wire.h pentru scrierea pe LCD

Ca idee generală, componentele interacționează prin variabile partajate, declarate volatile sau static. Pe lângă funcțiile clasice Arduino utilizate, cele mai interesante concepte software de care m-am folosit au fost:

  1. Pin Change Interrupt (Timer 2)
  2. PWM (Compare Match Interrupt, Timer 0)
  3. ADC

Workflow

Logica programului este ilustrată în schema de mai jos. Modul de funcționare default este modul 1, cu mișcarea în funcție de potențiometru.

Întrerupere Pin Change pe Timer 2

Pentru întreruperea de tip Pin Change de la buton, am folosit Timer-ul 2. Aici aflăm practic modul de funcționare. Inițial, am făcut următoarele configurări:

PCICR |= B00000100;   // set Timer 2
PCMSK2 |= B10000000;  // PCINT23 = PD7

Așa arată rutina de întrerupere. Am dezactivat Timer-ul 0 în timpul apăsării pentru a evita interferențe.

ISR(PCINT2_vect) {

 int buttonStatus = digitalRead(button);  // Read the current button status
 
 if (buttonStatus && !buttonPressed) {   // Button was just pressed
 
  digitalWrite(led, !digitalRead(led));  // Toggle the yellow LED status
  buttonPressed = true;                  
  TIMSK0 &= ~_BV(OCIE0A);                // Disable Timer0 interrupt during button press
  
 } else if (!buttonStatus && buttonPressed) {  // Button was just released
   buttonPressed = false;                     
   TIMSK0 |= _BV(OCIE0A);                      // Enable Timer0 interrupt during button press
 }
}

Întrerupere Compare Match pentru Timer 0 (PWM)

Ideea aici a fost să mișc servomotorul în poziții cât mai random, la un interval regulat, atunci când senzorul detectează mișcarea unui obiect apropiat. În fucția setup(), am făcut următoarele inițializări:

TCCR0A = _BV(COM0B1) | _BV(WGM00) | _BV(WGM01);  // Non-Inverting Fast PWM Mode, Fast PWM, 0-255, resets to 0
TCCR0B = _BV(WGM02) | _BV(CS02) | _BV(CS00);     // Waveform Generation Mode, Prescaler 1024
OCR0A = 31;                                      // Set TOP value
OCR0B = 15;                                      // Set initial value
TIMSK0 |= _BV(OCIE0A);                           // Enable Timer 0 Compare Match
sei();                                           // Enable interrupts

Calcule internal counter frequency

f_int = 16,000,000 / (1024 * (255 + 1)) = 16,000,000 / 262144 = 61.0352 Hz

Rutina de întrerupere, modul 2 de funcționare. În funcție de valoarea counter-ului Timer, calculez o nouă poziție de deplasare și un nou unghi pentru mișcarea servomotorului.

ISR(TIMER0_COMPA_vect) {
 Time++;                     // Global volatile variable
 if (Time == 20) {
  
   // Move servo
   if (forward) {
     pos += 5;               // Increment position
     if (pos >= 180)
       forward = false;      // Reached the maximum position, change direction
   } else {
     pos -= 5;               // Decrement position
     if (pos <= 0)
       forward = true;       // Reached the starting position, change direction
   }
   move = true;
   Time = 0;                 // Reset timer
 }
}

ADC

După stabilizarea potențiometrului cu ajutorul condensatorului, am configurat citirea valorii în felul următor:

ADMUX |= B01000000;      // 5 V
ADCSRA |= B01000111;     // Prescaler 128 and Enable
ADCSRA |= (1 << ADSC);   // ADC Start Conversion
while (!(ADCSRA & (1 << ADIF))); // Wait
uint16_t result = ADC;

Apoi transform valoarea citită într-un unghi pentru servomotor, folosind funcția map. În cod se observă utilizarea unei formule pentru aproximarea rezultatului, întrucât potențiometrul, chiar și cu acel condensator în paralel, avea momente de instabilitate.

static uint16_t smoothedResult = result; 
const float smoothingFactor = 0.5;        // Smoothing factor - for more control over servo
smoothedResult = smoothingFactor * smoothedResult + (1 - smoothingFactor) * result;
int servoPosition = map(smoothedResult, 0, 1023, 0, 180); // map value to get angle
myServo.write(servoPosition); // move servo

Restul funcțiilor, elementele de sincronizare și dependențele dintre variabile sunt explicate mai detaliat în README-ul și codul din arhivă:

Rezultate Obţinute

Rezultatul este o jucărie funcțională, cu un design atractiv, care îi oferă mobilitate, foarte interesantă pentru pisici, așa cum se poate observa și din demo-ul de mai sus!

Consider că rezultatul final depășește simularea inițială, cu funcții Arduino, care bifa criteriile de funcționalitate, însă la capitolul feature-uri și fiabilitate lăsa de dorit.

Concluzii

A fost un proiect foarte interesant, primul de până acum care implică componente fizice și lucru manual pentru design. Am lucrat cu mare drag la el, mai ales având în vedere scopul său și m-am bucurat foarte tare să îmi văd ideea pusă în practică după atâtea săptămâni. Bineînțeles, au existat și o serie de situații și evenimente neprevăzute, din care am putut trage o serie de concluzii:

Download

Arhiva cu codul sursă și README cu detalii despre implementare:

automatic_cat_toy_v2.zip

Jurnal

  • 20 Aprilie - Alegere temă proiect
  • 26 Aprilie - Confirmare temă, discuție la laborator
  • 5 Mai - Documentație Inițială
  • 6 Mai - Comandă piese
  • 9-13 Mai - Livrări
  • 14 Mai - Magazin fizic - ServoMotor, Led-uri, Rezistențe
  • 21 Mai - Hardware și funcționalități de bază
  • 22 Mai - Implementare logică și AVR
  • 24 Mai - Implementare buton întrerupere Pin Change
  • 26 Mai - Întrerupere PWM, schimbări sumare hardware
  • 27-28 Mai - Design și Finalizare cod
  • 29 Mai - Finalizare Design, Introducere condensator, testare

Bibliografie/Resurse

Export to PDF