Smart Pet Feeder
Automatic food and water for pets, on an ATmega32 programmed straight from the registers.
- Year
- 2025
- Team
- Solo
- My role
- Firmware, solo
- Built with
- ATmega32, C/C++, Proteus
/simulator
Feed the pet yourself
HX711: 12,500
sensor: OK
Be the pet
Empty a bowl and watch the firmware react: water is checked first, then food. The servo keeps dispensing until the load cell reads 10,000 again, and the buzzer beeps while a bowl is low.
The simulator follows the logic of main.cpp. Fill amounts and speeds are illustrative.
/how
Food and water, each with its own loop
Food
An HX711 amplifier reads the load cell under the bowl. Below the threshold, the LCD shows “Low Food!”, the red LED lights and the servo swings the hatch open and shut, weighing again after every swing until the bowl is full. The manual button dispenses one portion on demand.
Water
A level sensor watches the water bowl. When it reads low, the pump runs and the buzzer keeps beeping until the sensor sees water again. Then the green LED comes back on and the LCD says “Water Full!”.
| Part | Pins |
|---|---|
| LCD (4-bit) | PD0 RS · PD1 E · PD4–PD6, PB3 data |
| HX711 load cell | PA0 data · PC3 clock |
| Servo (food hatch) | PD7, software PWM |
| Water sensor / pump | PB5 in · PB4 out |
| Status LEDs | PD3, PC0 food · PB7, PD2 water |
| Buzzer | PC1 |
| Manual-feed button | PC2, debounced |
/code
Talking to the hardware directly
No libraries: the load cell is read by clocking its 24 bits in by hand, and the servo is driven by timing each pulse in software.
100long HX711_read() {101long count = 0;102uint8_t i;103104PORTC &= ~(1<<HX711_SCK);105while(PINA & (1<<HX711_DT)); // Wait until DOUT LOW106107for (i=0; i<24; i++) {108PORTC |= (1<<HX711_SCK);109count = count << 1;110PORTC &= ~(1<<HX711_SCK);111if (PINA & (1<<HX711_DT)) count++;112}113114PORTC |= (1<<HX711_SCK);115count ^= 0x800000;116PORTC &= ~(1<<HX711_SCK);117118return count;119}
126void Servo_pulse(uint16_t us) {127PORTD |= (1<<SERVO_PIN);128while (us--) _delay_us(1);129PORTD &= ~(1<<SERVO_PIN);130_delay_ms(20);131}132133void Servo_set_angle(uint8_t angle) {134uint16_t pulse = 1000 + ((uint32_t)angle * 1000) / 180;135for (int i = 0; i < 50; i++) {136Servo_pulse(pulse);137}138}
Read the full source · SmartPetFeeder/main.cpp · 261 lines
1#define F_CPU 8000000UL2#include <avr/io.h>3#include <util/delay.h>45// LCD Pins6#define LCD_RS PD07#define LCD_E PD18#define LCD_D4 PD49#define LCD_D5 PD510#define LCD_D6 PD611#define LCD_D7 PB31213// Water system14#define WATER_SENSOR_PIN PB515#define WATER_PUMP_PIN PB416#define WATER_GREEN_LED PB717#define WATER_RED_LED PD218#define WATER_BUZZER PC11920// Food system21#define FOOD_GREEN_LED PD322#define FOOD_RED_LED PC023#define SERVO_PIN PD72425// HX71126#define HX711_DT PA027#define HX711_SCK PC32829// Manual Feed Button30#define MANUAL_FEED_BTN PC23132// LCD functions33void lcd_enable() {34PORTD |= (1<<LCD_E);35_delay_us(1);36PORTD &= ~(1<<LCD_E);37_delay_us(100);38}3940void lcd_send_nibble(uint8_t nibble) {41if (nibble & (1<<0)) PORTD |= (1<<LCD_D4); else PORTD &= ~(1<<LCD_D4);42if (nibble & (1<<1)) PORTD |= (1<<LCD_D5); else PORTD &= ~(1<<LCD_D5);43if (nibble & (1<<2)) PORTD |= (1<<LCD_D6); else PORTD &= ~(1<<LCD_D6);44if (nibble & (1<<3)) PORTB |= (1<<LCD_D7); else PORTB &= ~(1<<LCD_D7);45lcd_enable();46}4748void lcd_send_cmd(uint8_t cmd) {49PORTD &= ~(1<<LCD_RS);50lcd_send_nibble(cmd >> 4);51lcd_send_nibble(cmd & 0x0F);52_delay_ms(2);53}5455void lcd_send_data(uint8_t data) {56PORTD |= (1<<LCD_RS);57lcd_send_nibble(data >> 4);58lcd_send_nibble(data & 0x0F);59_delay_ms(2);60}6162void lcd_init() {63DDRD |= (1<<LCD_RS) | (1<<LCD_E) | (1<<LCD_D4) | (1<<LCD_D5) | (1<<LCD_D6);64DDRB |= (1<<LCD_D7);65_delay_ms(20);6667lcd_send_nibble(0x03); _delay_ms(5);68lcd_send_nibble(0x03); _delay_us(150);69lcd_send_nibble(0x03); _delay_ms(5);70lcd_send_nibble(0x02); _delay_ms(2);7172lcd_send_cmd(0x28);73lcd_send_cmd(0x0C);74lcd_send_cmd(0x06);75lcd_send_cmd(0x01);76_delay_ms(5);77}7879void lcd_clear() {80lcd_send_cmd(0x01);81_delay_ms(5);82}8384void lcd_gotoxy(uint8_t x, uint8_t y) {85lcd_send_cmd(0x80 + (y * 0x40) + x);86}8788void lcd_puts(const char* str) {89while(*str) {90lcd_send_data(*str++);91}92}9394// HX711 Functions95void HX711_init() {96DDRC |= (1<<HX711_SCK);97DDRA &= ~(1<<HX711_DT);98}99100long HX711_read() {101long count = 0;102uint8_t i;103104PORTC &= ~(1<<HX711_SCK);105while(PINA & (1<<HX711_DT)); // Wait until DOUT LOW106107for (i=0; i<24; i++) {108PORTC |= (1<<HX711_SCK);109count = count << 1;110PORTC &= ~(1<<HX711_SCK);111if (PINA & (1<<HX711_DT)) count++;112}113114PORTC |= (1<<HX711_SCK);115count ^= 0x800000;116PORTC &= ~(1<<HX711_SCK);117118return count;119}120121// Servo PWM122void Servo_init() {123DDRD |= (1<<SERVO_PIN);124}125126void Servo_pulse(uint16_t us) {127PORTD |= (1<<SERVO_PIN);128while (us--) _delay_us(1);129PORTD &= ~(1<<SERVO_PIN);130_delay_ms(20);131}132133void Servo_set_angle(uint8_t angle) {134uint16_t pulse = 1000 + ((uint32_t)angle * 1000) / 180;135for (int i = 0; i < 50; i++) {136Servo_pulse(pulse);137}138}139140// Initialization141void init_ports() {142// Outputs143DDRB |= (1<<WATER_PUMP_PIN) | (1<<WATER_GREEN_LED) | (1<<LCD_D7);144DDRD |= (1<<FOOD_GREEN_LED) | (1<<WATER_RED_LED) | (1<<SERVO_PIN) | (1<<LCD_RS) | (1<<LCD_E) | (1<<LCD_D4) | (1<<LCD_D5) | (1<<LCD_D6);145DDRC |= (1<<WATER_BUZZER) | (1<<FOOD_RED_LED);146147// Inputs148DDRB &= ~(1<<WATER_SENSOR_PIN);149DDRC &= ~(1<<MANUAL_FEED_BTN);150}151152// Buzzer simple alert (for LOW condition only)153void sound_alert() {154PORTC |= (1<<WATER_BUZZER);155_delay_ms(300);156PORTC &= ~(1<<WATER_BUZZER);157_delay_ms(300);158}159160// Water Control161void water_dispense() {162if (!(PINB & (1<<WATER_SENSOR_PIN))) {163lcd_clear();164lcd_gotoxy(0,0);165lcd_puts("Water Low!");166167PORTD |= (1<<WATER_RED_LED);168PORTB &= ~(1<<WATER_GREEN_LED); // OFF green169170sound_alert(); // Alert once171172PORTB |= (1<<WATER_PUMP_PIN);173174while (!(PINB & (1<<WATER_SENSOR_PIN))) {175lcd_gotoxy(0,1);176lcd_puts("Filling Water..");177_delay_ms(500);178sound_alert(); // ?? Keep beeping while low179}180181PORTB &= ~(1<<WATER_PUMP_PIN);182PORTB |= (1<<WATER_GREEN_LED); // Green ON183PORTD &= ~(1<<WATER_RED_LED); // Red OFF184185lcd_clear();186lcd_gotoxy(0,0);187lcd_puts("Water Full!");188_delay_ms(1000);189}190}191192// Food Control193void food_dispense() {194long weight = HX711_read();195uint8_t manual = 0;196197// Check if the manual feed button is pressed (low)198if (!(PINC & (1<<MANUAL_FEED_BTN))) {199_delay_ms(50);200if (!(PINC & (1<<MANUAL_FEED_BTN))) {201manual = 1;202}203}204205// Check if food is low or manual feed is pressed206if (weight < 10000 || manual) {207lcd_clear();208lcd_gotoxy(0,0);209lcd_puts("Low Food!");210lcd_gotoxy(0,1);211lcd_puts("Dispensing...");212213// Sound the buzzer when food is low (same as water)214sound_alert();215216PORTC |= (1<<FOOD_RED_LED);217PORTD &= ~(1<<FOOD_GREEN_LED);218219// Dispense food using servo220do {221if (!manual) {222sound_alert(); // ?? Beep while food low, not manual223}224225Servo_set_angle(90); // Dispense food226_delay_ms(1000);227Servo_set_angle(0); // Reset servo position228_delay_ms(1000);229230weight = HX711_read(); // Check weight after each dispense action231} while (weight < 10000 && !manual); // Repeat until food is full or manual action is done232233PORTD |= (1<<FOOD_GREEN_LED);234PORTC &= ~(1<<FOOD_RED_LED);235236lcd_clear();237lcd_gotoxy(0,0);238lcd_puts("Food Bowl Full");239_delay_ms(1000);240}241}242243// Main loop244int main(void)245{246init_ports();247lcd_init();248HX711_init();249Servo_init();250251lcd_clear();252lcd_gotoxy(0,0);253lcd_puts("Smart Pet Feeder");254255while (1)256{257water_dispense(); // Check and dispense water258food_dispense(); // Check and dispense food259_delay_ms(500);260}261}