This shows you the differences between two versions of the page.
pm:prj2025:vstoica:aila.zeitouni [2025/05/25 23:17] aila.zeitouni [Software Design] |
pm:prj2025:vstoica:aila.zeitouni [2025/05/26 16:59] (current) aila.zeitouni [Milestone 3 - Raport asupra implementării software] |
||
---|---|---|---|
Line 110: | Line 110: | ||
<note tip> | <note tip> | ||
- | #include <Arduino.h> | + | #include <avr/io.h> |
- | #include <Servo.h> | + | #include <util/delay.h> |
- | #include <Wire.h> | + | #include <stdint.h> |
- | #include <LiquidCrystal_PCF8574.h> // Installed manually in lib/ | + | #include <stdbool.h> |
+ | #include "lcd_i2c.h" | ||
+ | #include "twi.h" | ||
+ | // Pin defines matching your Arduino sketch | ||
+ | #define TRIG_PIN PD6 // D6 | ||
+ | #define ECHO_PIN PD7 // D7 | ||
+ | #define SERVO_PIN PB1 // D9 (OC1A) | ||
+ | #define BUZZER_PIN PB0 // D8 | ||
- | // Pin Definitions | + | #define DISTANCE_OPEN_THRESHOLD 15 |
- | const int TRIG_PIN = 6; | + | #define DISTANCE_TRASH_THRESHOLD 8 |
- | const int ECHO_PIN = 7; | + | #define TRASH_DETECT_DELAY_MS 3000 |
- | const int SERVO_PIN = 9; | + | |
- | const int BUZZER_PIN = 8; | + | |
- | // Constants | + | #define SERVO_OPEN_ANGLE 0 |
- | const int DISTANCE_OPEN_THRESHOLD = 15; // in cm | + | #define SERVO_CLOSE_ANGLE 180 |
- | const int DISTANCE_TRASH_THRESHOLD = 8; // for thanking | + | |
- | const int SERVO_OPEN_ANGLE = 0; | + | |
- | const int SERVO_CLOSE_ANGLE = 90; | + | |
- | const int TRASH_DETECT_DELAY = 3000; // 3 seconds before next "thank you" | + | |
- | // Components | + | void setup_servo_pwm() { |
- | Servo lidServo; | + | DDRB |= (1 << SERVO_PIN); // PB1 as output |
- | LiquidCrystal_PCF8574 lcd(0x27); | + | TCCR1A |= (1 << COM1A1) | (1 << WGM11); |
- | float distance_cm; | + | TCCR1B |= (1 << WGM12) | (1 << WGM13) | (1 << CS11); // prescaler 8 |
- | unsigned long lastTrashDetectTime = 0; | + | ICR1 = 19999; // 20 ms PWM |
- | float measureDistance(); | + | } |
- | void buzz(int times); | + | |
- | void setup() { | + | |
- | Serial.begin(9600); | + | |
- | tone(8, 1000); | + | |
- | // Sensor setup | + | |
- | pinMode(TRIG_PIN, OUTPUT); | + | |
- | pinMode(ECHO_PIN, INPUT); | + | |
- | // Buzzer | + | void set_servo_angle(uint8_t angle) { |
- | pinMode(BUZZER_PIN, OUTPUT); | + | OCR1A = (ICR1 * (1000 + ((uint32_t)angle * 1000 / 180))) / 20000; |
- | digitalWrite(BUZZER_PIN, LOW); | + | } |
- | // Servo setup | + | uint16_t measure_distance_cm() { |
- | lidServo.attach(SERVO_PIN); | + | // Send trigger pulse |
- | lidServo.write(SERVO_CLOSE_ANGLE); | + | PORTD &= ~(1 << TRIG_PIN); |
+ | _delay_us(2); | ||
+ | PORTD |= (1 << TRIG_PIN); | ||
+ | _delay_us(10); | ||
+ | PORTD &= ~(1 << TRIG_PIN); | ||
- | // LCD setup | + | // Wait for echo to go high |
- | lcd.begin(16, 2); | + | uint16_t timeout = 30000; |
- | lcd.setBacklight(255); | + | while (!(PIND & (1 << ECHO_PIN)) && timeout--) _delay_us(1); |
- | lcd.setCursor(0, 0); | + | if (timeout == 0) return 0; |
- | lcd.print(" Smart Stash Can "); | + | |
- | lcd.setCursor(0, 1); | + | // Measure how long echo is high |
- | lcd.print(" Initializing "); | + | uint32_t count = 0; |
- | delay(2000); | + | while ((PIND & (1 << ECHO_PIN)) && count < 30000) { |
- | lcd.clear(); | + | _delay_us(1); |
+ | count++; | ||
+ | } | ||
+ | |||
+ | // Convert to cm | ||
+ | return (uint16_t)((count * 0.0343) / 2); | ||
} | } | ||
- | void loop() { | + | void buzz(uint8_t times) { |
- | distance_cm = measureDistance(); | + | for (uint8_t i = 0; i < times; i++) { |
+ | PORTB |= (1 << BUZZER_PIN); | ||
+ | _delay_ms(300); | ||
+ | PORTB &= ~(1 << BUZZER_PIN); | ||
+ | _delay_ms(200); | ||
+ | } | ||
+ | } | ||
+ | #include "lcd_i2c.h" | ||
+ | #include "twi.h" | ||
- | Serial.print("Distance: "); | + | int main(void) { |
- | Serial.print(distance_cm); | + | // Setup TRIG as output, ECHO as input |
- | Serial.println(" cm"); | + | DDRD |= (1 << TRIG_PIN); |
+ | DDRD &= ~(1 << ECHO_PIN); | ||
- | if (distance_cm > 0 && distance_cm < DISTANCE_OPEN_THRESHOLD) { | + | // Setup buzzer |
- | // Open lid | + | DDRB |= (1 << BUZZER_PIN); |
- | lcd.setCursor(0, 0); | + | PORTB &= ~(1 << BUZZER_PIN); |
- | lcd.print("Opening lid "); | + | |
- | lidServo.write(SERVO_OPEN_ANGLE); | + | |
- | // 🔊 Buzzer beep on open | + | // Init I2C and LCD |
- | buzz(1); | + | twi_init(); |
- | } else { | + | lcd_init(); |
- | // Close lid | + | |
- | lcd.setCursor(0, 0); | + | |
- | //lcd.print("Waiting... "); | + | |
- | lidServo.write(SERVO_CLOSE_ANGLE); | + | |
- | } | + | |
- | // Trash detection and second thank-you buzzer | + | lcd_clear(); |
- | if (distance_cm > 0 && distance_cm < DISTANCE_TRASH_THRESHOLD) { | + | lcd_set_cursor(0, 0); |
- | if (millis() - lastTrashDetectTime > TRASH_DETECT_DELAY) { | + | lcd_print("Smart Stash Can"); |
- | lcd.setCursor(0, 1); | + | lcd_set_cursor(0, 1); |
- | lcd.print(" Thank you! "); | + | lcd_print(" Initializing "); |
- | buzz(2); // 2 short beeps | + | _delay_ms(2000); |
- | lastTrashDetectTime = millis(); | + | lcd_clear(); |
- | } | + | |
- | } else { | + | |
- | lcd.setCursor(0, 1); | + | |
- | lcd.print("Scanning area "); | + | |
- | } | + | |
- | delay(300); | + | // Init servo |
- | } | + | setup_servo_pwm(); |
- | + | set_servo_angle(SERVO_CLOSE_ANGLE); | |
- | float measureDistance() { | + | uint32_t last_thank_time = 0; |
- | digitalWrite(TRIG_PIN, LOW); | + | |
- | delayMicroseconds(2); | + | |
- | digitalWrite(TRIG_PIN, HIGH); | + | |
- | delayMicroseconds(10); | + | |
- | digitalWrite(TRIG_PIN, LOW); | + | |
- | float duration = pulseIn(ECHO_PIN, HIGH); | + | |
- | return 0.017 * duration; | + | |
- | } | + | |
- | void buzz(int times) { | + | while (1) { |
- | for (int i = 0; i < times; i++) { | + | uint16_t distance = measure_distance_cm(); |
- | digitalWrite(BUZZER_PIN, HIGH); | + | |
- | delay(300); // increase duration | + | if (distance > 0 && distance < DISTANCE_OPEN_THRESHOLD) { |
- | digitalWrite(BUZZER_PIN, LOW); | + | set_servo_angle(SERVO_OPEN_ANGLE); |
- | delay(200); | + | lcd_set_cursor(0, 0); |
- | } | + | lcd_print("Opening lid "); |
+ | buzz(1); | ||
+ | } else { | ||
+ | set_servo_angle(SERVO_CLOSE_ANGLE); | ||
+ | lcd_set_cursor(0, 0); | ||
+ | lcd_print("Lid closed "); | ||
+ | } | ||
+ | |||
+ | if (distance > 0 && distance < DISTANCE_TRASH_THRESHOLD) { | ||
+ | if ((last_thank_time + TRASH_DETECT_DELAY_MS) < 60000) { | ||
+ | lcd_set_cursor(0, 1); | ||
+ | lcd_print(" Thank you! "); | ||
+ | buzz(2); | ||
+ | last_thank_time = 0; | ||
+ | } | ||
+ | } else { | ||
+ | lcd_set_cursor(0, 1); | ||
+ | lcd_print("Scanning area "); | ||
+ | last_thank_time += 300; | ||
+ | } | ||
+ | |||
+ | _delay_ms(300); | ||
+ | } | ||
} | } | ||
+ | |||
</note> | </note> | ||
+ | |||
+ | |||
+ | |||
+ | ====== Milestone 3 - Raport asupra implementării software ====== | ||
+ | |||
+ | === Stadiul actual al implementării === | ||
+ | |||
+ | Software-ul pentru proiectul Smart Stash Can este funcțional și a fost testat cu succes pe hardware-ul final. Funcționalitățile implementate includ: | ||
+ | |||
+ | * Detectarea distanței utilizând senzorul ultrasonic HC-SR04 (TRIG pe PD6, ECHO pe PD7) | ||
+ | * Controlul capacului prin servomotor acționat PWM (OC1A - PB1) | ||
+ | * Feedback sonor cu ajutorul unui buzzer (PB0) | ||
+ | * Afișaj LCD I2C (cu PCF8574, adresă 0x27) pentru interfața cu utilizatorul | ||
+ | * Cod modular scris în C bare-metal cu acces direct la registrele perifericelor | ||
+ | * Demonstrație completă validată experimental în laborator | ||
+ | |||
+ | --- | ||
+ | |||
+ | === Biblioteci și drivere utilizate === | ||
+ | |||
+ | ^ Componentă ^ Bibliotecă / Driver ^ Justificare ^ | ||
+ | | Afișaj LCD | lcd_i2c.c / lcd_i2c.h (scris de noi) | Implementare ușoară și eficientă pentru PCF8574, fără dependențe de Arduino | | ||
+ | | Magistrală I2C | twi.c / twi.h (din laborator) | Permite control complet asupra comunicației TWI | | ||
+ | | UART (debug) | usart.c / usart.h | Folosit pentru mesaje de test și jurnalizare serială | | ||
+ | | Control servomotor | PWM cu Timer1 | Precizie ridicată folosind OCR1A și ICR1 fără delay-uri software | | ||
+ | |||
+ | Fiecare bibliotecă a fost selectată pentru simplitate, eficiență și integrare directă cu perifericele platformei ATmega328P. | ||
+ | |||
+ | --- | ||
+ | |||
+ | === Element de noutate al proiectului === | ||
+ | |||
+ | Acest proiect se diferențiază prin abordarea complet bare-metal, fără utilizarea bibliotecilor Arduino. Interacțiunea între senzor, servomotor, buzzer și afișaj este realizată exclusiv prin programare directă a registrelor. Se obține astfel un control total asupra funcționalității, temporizării și performanței sistemului, într-un format educativ și extensibil. | ||
+ | |||
+ | --- | ||
+ | |||
+ | === Justificarea utilizării funcționalităților din laborator === | ||
+ | |||
+ | ^ Laborator ^ Funcționalitate utilizată ^ Aplicare în proiect ^ | ||
+ | | Lab 0 | GPIO | TRIG și ECHO pentru senzor, ieșire pentru buzzer | | ||
+ | | Lab 1 | UART | Trimiterea mesajelor debug către terminal serial | | ||
+ | | Lab 3 | Timere și PWM | Control precis al servomotorului cu Timer1, canal OC1A | | ||
+ | | Lab 6 | I2C (TWI) | Comunicație cu afișajul LCD prin interfața I2C (PCF8574) | | ||
+ | |||
+ | Fiecare funcționalitate din laborator a fost integrată într-un mod practic pentru a forma un sistem embedded complet funcțional. | ||
+ | |||
+ | --- | ||
+ | |||
+ | === Structura proiectului și validare === | ||
+ | |||
+ | **Fișiere principale:** | ||
+ | |||
+ | * `main.c` – logica principală, secvența de control | ||
+ | * `servo.c/h` – control PWM pentru servomotor | ||
+ | * `twi.c/h` – comunicație I2C (TWI) | ||
+ | * `lcd_i2c.c/h` – afișare pe LCD prin PCF8574 | ||
+ | * `usart.c/h` – trimiterea mesajelor seriale | ||
+ | |||
+ | **Validare:** | ||
+ | * Testare modulară: fiecare componentă a fost verificată individual | ||
+ | * Testare integrată: comportamentul întregului sistem a fost observat în timp real | ||
+ | * Compararea distanțelor măsurate cu o riglă fizică | ||
+ | * Verificarea reacției servo în funcție de distanță | ||
+ | * Confirmarea clarității mesajelor LCD și a actualizării cursorului | ||
+ | |||
+ | --- | ||
+ | |||
+ | === Calibrarea senzorului de distanță === | ||
+ | |||
+ | Senzorul ultrasonic HC-SR04 a fost calibrat folosind măsurători directe: | ||
+ | |||
+ | * Formula de conversie: `(durata în µs × 0.0343) / 2` | ||
+ | * Pulsurile TRIG și ECHO au fost gestionate cu `_delay_us()` pentru precizie | ||
+ | * Praguri stabilite experimental: | ||
+ | * Deschiderea capacului la < 15 cm | ||
+ | * Mesaj de mulțumire la < 8 cm | ||
+ | * Zgomotul de măsurare a fost redus prin cicluri scurte și perioade de eșantionare stabile (300 ms) | ||
+ | |||
+ | --- | ||
+ | |||
+ | === Optimizări realizate === | ||
+ | |||
+ | * PWM hardware în loc de temporizări software pentru controlul servomotorului | ||
+ | * I2C implementat low-level fără librării externe, pentru control precis | ||
+ | * Suprimarea “ghosting”-ului pe LCD prin scriere completă a liniilor cu spații | ||
+ | * Utilizarea exclusivă a variabilelor statice, fără alocare dinamică | ||
+ | * Funcția de măsurare a distanței optimizată pentru precizie și timeout sigur | ||
+ | * Structura buclei principale minimizează întârzierile și optimizează reacția sistemului | ||
+ | |||
+ | --- | ||
+ | |||
+ | === Video demonstrativ === | ||
+ | |||
+ | O demonstrație video a întregului proiect, cu explicații și comportament funcțional, poate fi vizualizată la: | ||
+ | |||
+ | |||
+ | |||
+ | --- | ||
===== Rezultate Obţinute ===== | ===== Rezultate Obţinute ===== |