All work
Embedded system2025

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
0
Arduino libraries: registers only
24-bit
load-cell reads, bit-banged
8 MHz
ATmega32 clock
261
lines of C/C++

/simulator

Feed the pet yourself

Smart Pet Feeder
Food bowl

HX711: 12,500

Water bowl

sensor: OK

Food OKFood lowWater OKWater low
Buzzer

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!”.

Pin map
PartPins
LCD (4-bit)PD0 RS · PD1 E · PD4–PD6, PB3 data
HX711 load cellPA0 data · PC3 clock
Servo (food hatch)PD7, software PWM
Water sensor / pumpPB5 in · PB4 out
Status LEDsPD3, PC0 food · PB7, PD2 water
BuzzerPC1
Manual-feed buttonPC2, 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.

main.cpp · HX711_read
100long HX711_read() {
101 long count = 0;
102 uint8_t i;
103
104 PORTC &= ~(1<<HX711_SCK);
105 while(PINA & (1<<HX711_DT)); // Wait until DOUT LOW
106
107 for (i=0; i<24; i++) {
108 PORTC |= (1<<HX711_SCK);
109 count = count << 1;
110 PORTC &= ~(1<<HX711_SCK);
111 if (PINA & (1<<HX711_DT)) count++;
112 }
113
114 PORTC |= (1<<HX711_SCK);
115 count ^= 0x800000;
116 PORTC &= ~(1<<HX711_SCK);
117
118 return count;
119}
main.cpp · servo
126void Servo_pulse(uint16_t us) {
127 PORTD |= (1<<SERVO_PIN);
128 while (us--) _delay_us(1);
129 PORTD &= ~(1<<SERVO_PIN);
130 _delay_ms(20);
131}
132
133void Servo_set_angle(uint8_t angle) {
134 uint16_t pulse = 1000 + ((uint32_t)angle * 1000) / 180;
135 for (int i = 0; i < 50; i++) {
136 Servo_pulse(pulse);
137 }
138}
Read the full source · SmartPetFeeder/main.cpp · 261 lines
SmartPetFeeder/main.cpp
1#define F_CPU 8000000UL
2#include <avr/io.h>
3#include <util/delay.h>
4
5// LCD Pins
6#define LCD_RS PD0
7#define LCD_E PD1
8#define LCD_D4 PD4
9#define LCD_D5 PD5
10#define LCD_D6 PD6
11#define LCD_D7 PB3
12
13// Water system
14#define WATER_SENSOR_PIN PB5
15#define WATER_PUMP_PIN PB4
16#define WATER_GREEN_LED PB7
17#define WATER_RED_LED PD2
18#define WATER_BUZZER PC1
19
20// Food system
21#define FOOD_GREEN_LED PD3
22#define FOOD_RED_LED PC0
23#define SERVO_PIN PD7
24
25// HX711
26#define HX711_DT PA0
27#define HX711_SCK PC3
28
29// Manual Feed Button
30#define MANUAL_FEED_BTN PC2
31
32// LCD functions
33void lcd_enable() {
34 PORTD |= (1<<LCD_E);
35 _delay_us(1);
36 PORTD &= ~(1<<LCD_E);
37 _delay_us(100);
38}
39
40void lcd_send_nibble(uint8_t nibble) {
41 if (nibble & (1<<0)) PORTD |= (1<<LCD_D4); else PORTD &= ~(1<<LCD_D4);
42 if (nibble & (1<<1)) PORTD |= (1<<LCD_D5); else PORTD &= ~(1<<LCD_D5);
43 if (nibble & (1<<2)) PORTD |= (1<<LCD_D6); else PORTD &= ~(1<<LCD_D6);
44 if (nibble & (1<<3)) PORTB |= (1<<LCD_D7); else PORTB &= ~(1<<LCD_D7);
45 lcd_enable();
46}
47
48void lcd_send_cmd(uint8_t cmd) {
49 PORTD &= ~(1<<LCD_RS);
50 lcd_send_nibble(cmd >> 4);
51 lcd_send_nibble(cmd & 0x0F);
52 _delay_ms(2);
53}
54
55void lcd_send_data(uint8_t data) {
56 PORTD |= (1<<LCD_RS);
57 lcd_send_nibble(data >> 4);
58 lcd_send_nibble(data & 0x0F);
59 _delay_ms(2);
60}
61
62void lcd_init() {
63 DDRD |= (1<<LCD_RS) | (1<<LCD_E) | (1<<LCD_D4) | (1<<LCD_D5) | (1<<LCD_D6);
64 DDRB |= (1<<LCD_D7);
65 _delay_ms(20);
66
67 lcd_send_nibble(0x03); _delay_ms(5);
68 lcd_send_nibble(0x03); _delay_us(150);
69 lcd_send_nibble(0x03); _delay_ms(5);
70 lcd_send_nibble(0x02); _delay_ms(2);
71
72 lcd_send_cmd(0x28);
73 lcd_send_cmd(0x0C);
74 lcd_send_cmd(0x06);
75 lcd_send_cmd(0x01);
76 _delay_ms(5);
77}
78
79void lcd_clear() {
80 lcd_send_cmd(0x01);
81 _delay_ms(5);
82}
83
84void lcd_gotoxy(uint8_t x, uint8_t y) {
85 lcd_send_cmd(0x80 + (y * 0x40) + x);
86}
87
88void lcd_puts(const char* str) {
89 while(*str) {
90 lcd_send_data(*str++);
91 }
92}
93
94// HX711 Functions
95void HX711_init() {
96 DDRC |= (1<<HX711_SCK);
97 DDRA &= ~(1<<HX711_DT);
98}
99
100long HX711_read() {
101 long count = 0;
102 uint8_t i;
103
104 PORTC &= ~(1<<HX711_SCK);
105 while(PINA & (1<<HX711_DT)); // Wait until DOUT LOW
106
107 for (i=0; i<24; i++) {
108 PORTC |= (1<<HX711_SCK);
109 count = count << 1;
110 PORTC &= ~(1<<HX711_SCK);
111 if (PINA & (1<<HX711_DT)) count++;
112 }
113
114 PORTC |= (1<<HX711_SCK);
115 count ^= 0x800000;
116 PORTC &= ~(1<<HX711_SCK);
117
118 return count;
119}
120
121// Servo PWM
122void Servo_init() {
123 DDRD |= (1<<SERVO_PIN);
124}
125
126void Servo_pulse(uint16_t us) {
127 PORTD |= (1<<SERVO_PIN);
128 while (us--) _delay_us(1);
129 PORTD &= ~(1<<SERVO_PIN);
130 _delay_ms(20);
131}
132
133void Servo_set_angle(uint8_t angle) {
134 uint16_t pulse = 1000 + ((uint32_t)angle * 1000) / 180;
135 for (int i = 0; i < 50; i++) {
136 Servo_pulse(pulse);
137 }
138}
139
140// Initialization
141void init_ports() {
142 // Outputs
143 DDRB |= (1<<WATER_PUMP_PIN) | (1<<WATER_GREEN_LED) | (1<<LCD_D7);
144 DDRD |= (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);
145 DDRC |= (1<<WATER_BUZZER) | (1<<FOOD_RED_LED);
146
147 // Inputs
148 DDRB &= ~(1<<WATER_SENSOR_PIN);
149 DDRC &= ~(1<<MANUAL_FEED_BTN);
150}
151
152// Buzzer simple alert (for LOW condition only)
153void sound_alert() {
154 PORTC |= (1<<WATER_BUZZER);
155 _delay_ms(300);
156 PORTC &= ~(1<<WATER_BUZZER);
157 _delay_ms(300);
158}
159
160// Water Control
161void water_dispense() {
162 if (!(PINB & (1<<WATER_SENSOR_PIN))) {
163 lcd_clear();
164 lcd_gotoxy(0,0);
165 lcd_puts("Water Low!");
166
167 PORTD |= (1<<WATER_RED_LED);
168 PORTB &= ~(1<<WATER_GREEN_LED); // OFF green
169
170 sound_alert(); // Alert once
171
172 PORTB |= (1<<WATER_PUMP_PIN);
173
174 while (!(PINB & (1<<WATER_SENSOR_PIN))) {
175 lcd_gotoxy(0,1);
176 lcd_puts("Filling Water..");
177 _delay_ms(500);
178 sound_alert(); // ?? Keep beeping while low
179 }
180
181 PORTB &= ~(1<<WATER_PUMP_PIN);
182 PORTB |= (1<<WATER_GREEN_LED); // Green ON
183 PORTD &= ~(1<<WATER_RED_LED); // Red OFF
184
185 lcd_clear();
186 lcd_gotoxy(0,0);
187 lcd_puts("Water Full!");
188 _delay_ms(1000);
189 }
190}
191
192// Food Control
193void food_dispense() {
194 long weight = HX711_read();
195 uint8_t manual = 0;
196
197 // Check if the manual feed button is pressed (low)
198 if (!(PINC & (1<<MANUAL_FEED_BTN))) {
199 _delay_ms(50);
200 if (!(PINC & (1<<MANUAL_FEED_BTN))) {
201 manual = 1;
202 }
203 }
204
205 // Check if food is low or manual feed is pressed
206 if (weight < 10000 || manual) {
207 lcd_clear();
208 lcd_gotoxy(0,0);
209 lcd_puts("Low Food!");
210 lcd_gotoxy(0,1);
211 lcd_puts("Dispensing...");
212
213 // Sound the buzzer when food is low (same as water)
214 sound_alert();
215
216 PORTC |= (1<<FOOD_RED_LED);
217 PORTD &= ~(1<<FOOD_GREEN_LED);
218
219 // Dispense food using servo
220 do {
221 if (!manual) {
222 sound_alert(); // ?? Beep while food low, not manual
223 }
224
225 Servo_set_angle(90); // Dispense food
226 _delay_ms(1000);
227 Servo_set_angle(0); // Reset servo position
228 _delay_ms(1000);
229
230 weight = HX711_read(); // Check weight after each dispense action
231 } while (weight < 10000 && !manual); // Repeat until food is full or manual action is done
232
233 PORTD |= (1<<FOOD_GREEN_LED);
234 PORTC &= ~(1<<FOOD_RED_LED);
235
236 lcd_clear();
237 lcd_gotoxy(0,0);
238 lcd_puts("Food Bowl Full");
239 _delay_ms(1000);
240 }
241}
242
243// Main loop
244int main(void)
245{
246 init_ports();
247 lcd_init();
248 HX711_init();
249 Servo_init();
250
251 lcd_clear();
252 lcd_gotoxy(0,0);
253 lcd_puts("Smart Pet Feeder");
254
255 while (1)
256 {
257 water_dispense(); // Check and dispense water
258 food_dispense(); // Check and dispense food
259 _delay_ms(500);
260 }
261}

/contact

Let’s design and build something people love using.

I’m looking for a UI/UX or frontend role on a team that ships. Email is the fastest way to reach me.

© 2026 Mayar Mohamed Issa. Designed and built by me, with Next.js, Tailwind CSS and Motion.