#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#define F_CPU 16000000UL
#define LCD_I2C_ADDR 0x27
// LCD control bits
#define LCD_BACKLIGHT 0x08
#define LCD_NOBACKLIGHT 0x00
#define En 0b00000100 // enable bit
#define Rw 0b00000010 // read/write bit
#define Rs 0b00000001 // register select bit
//define the dht pin
#define DHT22_PIN PD2
//blue
//define the led pin
#define LED_PIN PH4
//red
//define the led2 pin
#define LED2_PIN PE4
// define the buzzer pin
#define BUZZER_PIN PE5
#define BUZZER_PORT PORTB
#define BUZZER_DDR DDRB
// global variable for buzzer flag
uint8_t buzzer_flag = 0;
// define the HX711 pins for water and food load cells
#define WATER_HX711_DT_PIN PG5 // digital pin 4
#define WATER_HX711_SCK_PIN PA1 // digital pin A1
#define FOOD_HX711_DT_PIN PH3 // digital pin 6
#define FOOD_HX711_SCK_PIN PA0 // digital pin A0
// Define the keypad pins
#define R1 PH5 // Digital pin 8
#define R2 PH6 // Digital pin 9
#define R3 PB4 // Digital pin 10
#define R4 PB5 // Digital pin 11
#define C1 PC7 // Digital pin 30
#define C2 PC6 // Digital pin 31
#define C3 PC5 // Digital pin 32
#define C4 PC4 // Digital pin 33
void KEYPAD_init() {
DDRH |= (1 << R1) | (1 << R2); // set rows as output
DDRB |= (1 << R3) | (1 << R4); // set rows as output
DDRC &= ~((1 << C1) | (1 << C2) | (1 << C3) | (1 << C4));
PORTC |= (1 << C1) | (1 << C2) | (1 << C3) | (1 << C4);
}
char KEYPAD_getkey() {
const char keymap[4][4] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
for (uint8_t row = 0; row < 4; row++) {
PORTH |= (1 << R1) | (1 << R2);// set all rows to high
PORTB |= (1 << R3) | (1 << R4);
switch (row) {// Set current row to low
case 0: PORTH &= ~(1 << R1); break;
case 1: PORTH &= ~(1 << R2); break;
case 2: PORTB &= ~(1 << R3); break;
case 3: PORTB &= ~(1 << R4); break;
}
if (!(PINC & (1 << C1))) return keymap[row][0];
if (!(PINC & (1 << C2))) return keymap[row][1];
if (!(PINC & (1 << C3))) return keymap[row][2];
if (!(PINC & (1 << C4))) return keymap[row][3];
}
return 0;
}
// HX711
void HX711_init(void) {
// Initialize water load cell pins
DDRG &= ~(1 << WATER_HX711_DT_PIN);
DDRA |= (1 << WATER_HX711_SCK_PIN);
// Initialize food load cell pins
DDRH &= ~(1 << FOOD_HX711_DT_PIN);
DDRA |= (1 << FOOD_HX711_SCK_PIN);
}
long HX711_read(uint8_t dt_pin, uint8_t sck_pin) {
unsigned long count = 0;
unsigned char i;
PORTA &= ~(1 << sck_pin); // SCK pin low
while (PIND & (1 << dt_pin)); // Wait until DT pin is low
for (i = 0; i < 24; i++) {
PORTA |= (1 << sck_pin); // SCK pin high
_delay_us(1);
count = count << 1;
PORTA &= ~(1 << sck_pin); // SCK pin low
_delay_us(1);
if (PIND & (1 << dt_pin)) {
count++;
}
}
PORTA |= (1 << sck_pin); // SCK pin high
_delay_us(1);
PORTA &= ~(1 << sck_pin); // SCK pin low
_delay_us(1);
count ^= 0x800000; // Adjust for 24 bit reading
return count;
}
long HX711_get_value(uint8_t dt_pin, uint8_t sck_pin) {
long sum = 0;
for (int i = 0; i < 10; i++) {
sum += HX711_read(dt_pin, sck_pin);
}
return sum / 10;
}
float HX711_get_weight(uint8_t dt_pin, uint8_t sck_pin, long offset, float scale) {
long reading = HX711_get_value(dt_pin, sck_pin);
return (float)((reading - offset) / scale);
}
void LED_init(void)
{
DDRH |= (1 << LED_PIN);
}
void LED_on(void)
{
PORTH |= (1 << LED_PIN);
}
void LED_off(void)
{
PORTH &= ~(1 << LED_PIN);
}
//led2 functions
void LED2_init(void)
{
DDRE |= (1 << LED2_PIN);
}
void LED2_on(void)
{
PORTE |= (1 << LED2_PIN);
}
void LED2_off(void)
{
PORTE &= ~(1 << LED2_PIN);
}
void BUZZER_init(void) {
BUZZER_DDR |= (1 << BUZZER_PIN); //buzzer pin as output
}
void BUZZER_on(void) {
BUZZER_PORT |= (1 << BUZZER_PIN); // turn buzzer on
}
void BUZZER_off(void) {
BUZZER_PORT &= ~(1 << BUZZER_PIN); // turn buzzer off
}
void i2c_init(void) {
TWSR = 0x00;
TWBR = 0x47; // set bit rate
TWCR = (1 << TWEN); // enable TWI
}
void i2c_start(uint8_t address) {
TWCR = (1 << TWSTA) | (1 << TWEN) | (1 << TWINT); // START condition
while (!(TWCR & (1 << TWINT))); // wait for TWINT flag set
TWDR = address; // Load slave address
TWCR = (1 << TWEN) | (1 << TWINT); // start transmission
while (!(TWCR & (1 << TWINT)));
}
void i2c_write(uint8_t data) {
TWDR = data; // load data to TWDR register
TWCR = (1 << TWEN) | (1 << TWINT); // start transmission
while (!(TWCR & (1 << TWINT)));
}
void i2c_stop(void) {
TWCR = (1 << TWSTO) | (1 << TWEN) | (1 << TWINT); // send STOP condition
while (TWCR & (1 << TWSTO));
}
// LCD I2C functions
void LCD_init(uint8_t lcd_addr) {
_delay_ms(50);
LCD_send_command(0x33); // initialize LCD in 4-bit mode
LCD_send_command(0x32); // set to 4-bit mode
LCD_send_command(0x06);
LCD_send_command(0x0C);
LCD_send_command(0x28);
LCD_send_command(0x01); // clear display
_delay_ms(5);
}
void LCD_create_char(uint8_t location, uint8_t charmap[]) {
location &= 0x7; // 8 locations
LCD_send_command(0x40 | (location << 3));
for (int i = 0; i < 8; i++) {
LCD_send_data(charmap[i]);
}
}
void LCD_set_cursor(uint8_t row, uint8_t col) {
uint8_t row_offsets[] = {0x00, 0x40, 0x14, 0x54};
LCD_send_command(0x80 | (col + row_offsets[row]));
}
void LCD_print(const char* str) {
while (*str) {
LCD_send_data(*str++);
}
}
static void LCD_send_command(uint8_t command) {
LCD_write(command, 0);
}
static void LCD_send_data(uint8_t data) {
LCD_write(data, Rs);
}
static void LCD_write(uint8_t data, uint8_t mode) {
uint8_t high_nibble = data & 0xF0;
uint8_t low_nibble = (data << 4) & 0xF0;
i2c_start(LCD_I2C_ADDR << 1);
i2c_write(high_nibble | mode | LCD_BACKLIGHT | En);
i2c_write(high_nibble | mode | LCD_BACKLIGHT);
i2c_write(low_nibble | mode | LCD_BACKLIGHT | En);
i2c_write(low_nibble | mode | LCD_BACKLIGHT);
i2c_stop();
}
//DHT22
void DHT22_request() {
DDRD |= (1 << DHT22_PIN); // output mode
PORTD &= ~(1 << DHT22_PIN); // send low signal
_delay_ms(20);
PORTD |= (1 << DHT22_PIN); // senD high signal
_delay_us(40);
DDRD &= ~(1 << DHT22_PIN); // input mode
}
uint8_t DHT22_response() {
uint8_t response = 0;
DDRD &= ~(1 << DHT22_PIN); // input mode
_delay_us(40);
if (!(PIND & (1 << DHT22_PIN))) {
_delay_us(80);
if ((PIND & (1 << DHT22_PIN))) {
response = 1;
}
_delay_us(80);
}
return response;
}
uint8_t DHT22_receive_data() {
uint8_t data = 0;
for (int i = 0; i < 8; i++) {
while (!(PIND & (1 << DHT22_PIN))); // wait high level
_delay_us(30);
if (PIND & (1 << DHT22_PIN)) {
data = (data << 1) | 1;
} else {
data = (data << 1); // read 0
}
while (PIND & (1 << DHT22_PIN)); // wait high level
}
return data;
}
// Servo functions
void SERVO_init(void) {
DDRE |= (1 << PE3); // PE3 cikis
TCCR3A |= (1 << WGM31) | (1 << COM3A1); // Fast PWM modunu ayarla
TCCR3B |= (1 << WGM32) | (1 << WGM33) | (1 << CS31); // Prescaler 8
ICR3 = 39999; // TOP değeri 20ms periyot icin
}
void SERVO_set_position(int position) {
uint16_t pulse_width = (position * 11) + 1000; // konumu pulse genisliğine çevir
OCR3A = pulse_width;
}
int main()
{
i2c_init();
LCD_init(LCD_I2C_ADDR);
LED_init();
LED2_init();
BUZZER_init();
SERVO_init();
HX711_init();
KEYPAD_init();
// Create a custom character for the degree symbol
uint8_t degree_symbol[8] = {
0b00111,
0b00101,
0b00111,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000
};
LCD_create_char(0, degree_symbol);
char lcd_buffer[32];
LCD_set_cursor(0, 0);
//i think i cannot adjust these values, thats way i got ?
// tare and calibrate the water load cell
long water_offset = HX711_get_value(WATER_HX711_DT_PIN, WATER_HX711_SCK_PIN); // Tare the load cell
float water_scale = 2280.f; // calibration factor
// tare and calibrate the food load cell
long food_offset = HX711_get_value(FOOD_HX711_DT_PIN, FOOD_HX711_SCK_PIN); // Tare the load cell
float food_scale = 2280.f; // calibration factor
while (1)
{
char buffer[80];
uint8_t humidity_high, humidity_low, temp_high, temp_low, checksum;
DHT22_request();
if (DHT22_response())
{
humidity_high = DHT22_receive_data();
humidity_low = DHT22_receive_data();
temp_high = DHT22_receive_data();
temp_low = DHT22_receive_data();
checksum = DHT22_receive_data();
int humidity = (humidity_high << 8) | humidity_low;
int temperature = (temp_high << 8) | temp_low;
// Checksum control
if ((temp_high + temp_low) != checksum )
{
LCD_set_cursor(0, 0);
snprintf(lcd_buffer, sizeof(lcd_buffer), "Temperature: %d ", temperature / 10);
LCD_print(lcd_buffer);
LCD_send_data(0); // print degree symbol
LCD_print("C");
_delay_ms(500);
if ((temperature / 10) > 25) {
LED2_on(); // Turn on LED
} else {
LED2_off(); // Turn off LED
}
}
else
{
// Checksum error
LCD_set_cursor(0, 0);
BUZZER_on();
LCD_print("ERROR!");
_delay_ms(500);
BUZZER_off();
}
}
else
{
// DHT22 not responding
LCD_set_cursor(0, 0);
LCD_print("Cannot Read the Temperature!"); // DHT22 error
}
_delay_ms(500);
// Read water weight from load cell
float water_weight = HX711_get_weight(WATER_HX711_DT_PIN, WATER_HX711_SCK_PIN, water_offset, water_scale);
LCD_set_cursor(1, 0);
snprintf(lcd_buffer, sizeof(lcd_buffer), "Water: %.1f g", water_weight);
LCD_print(lcd_buffer);
// Read food weight from load cell
float food_weight = HX711_get_weight(FOOD_HX711_DT_PIN, FOOD_HX711_SCK_PIN, food_offset, food_scale);
LCD_set_cursor(2, 0);
snprintf(lcd_buffer, sizeof(lcd_buffer), "Food: %.1f g", food_weight);
LCD_print(lcd_buffer);
// Feeding mechanism control
if (food_weight < 30) {
if (!buzzer_flag) {
BUZZER_on(); // turn on the buzzer
buzzer_flag = 1; // set the flag to indicate buzzer activation
}
LED_on(); // turn on LED
_delay_ms(100);
LED_off();
// open the feeder (servo) -> 90 degrees
SERVO_set_position(90);
LCD_set_cursor(3, 0);
LCD_print("Opened the Feeder");
_delay_ms(500); // Wait for 500ms
// check the food weight again to see if its reach 30 or not
food_weight = HX711_get_weight(FOOD_HX711_DT_PIN, FOOD_HX711_SCK_PIN, food_offset, food_scale);
} else {
// reset the buzzer and LED flag when weight is not less than 30
// close the feeder (servo) -> 0 degrees
SERVO_set_position(0);
LCD_set_cursor(3, 0);
LCD_print("Closed the Feeder");
_delay_ms(500); // wait for 500ms
buzzer_flag = 0;
BUZZER_off(); //turn off the buzzer
}
_delay_ms(500);
}
return 0;
}
/*int main() {
// Initialize peripherals
i2c_init();
LCD_init(LCD_I2C_ADDR);
LED_init();
LED2_init();
BUZZER_init();
SERVO_init();
HX711_init();
KEYPAD_init();
// Create a custom character for the degree symbol
uint8_t degree_symbol[8] = {
0b00111,
0b00101,
0b00111,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000
};
LCD_create_char(0, degree_symbol);
char lcd_buffer[32];
LCD_set_cursor(0, 0);
// Tare and calibrate the water load cell
long water_offset = HX711_get_value(WATER_HX711_DT_PIN, WATER_HX711_SCK_PIN); // Tare the load cell
float water_scale = 2280.f; // calibration factor
// Tare and calibrate the food load cell
long food_offset = HX711_get_value(FOOD_HX711_DT_PIN, FOOD_HX711_SCK_PIN); // Tare the load cell
float food_scale = 2280.f; // calibration factor
// Initialize expected food amount
float expected_food_amount = 30.0; // Default value if no input from keypad
while (1) {
char buffer[80];
uint8_t humidity_high, humidity_low, temp_high, temp_low, checksum;
DHT22_request();
if (DHT22_response()) {
humidity_high = DHT22_receive_data();
humidity_low = DHT22_receive_data();
temp_high = DHT22_receive_data();
temp_low = DHT22_receive_data();
checksum = DHT22_receive_data();
int humidity = (humidity_high << 8) | humidity_low;
int temperature = (temp_high << 8) | temp_low;
// Checksum control
if ((temp_high + temp_low) != checksum) {
LCD_set_cursor(0, 0);
snprintf(lcd_buffer, sizeof(lcd_buffer), "Temperature: %d ", temperature / 10);
LCD_print(lcd_buffer);
LCD_send_data(0); // print degree symbol
LCD_print("C");
if (temperature / 10 > 25) {
LED2_on(); // Turn on LED
} else {
LED2_off(); // Turn off LED
}
} else {
// Checksum error
LCD_set_cursor(0, 0);
BUZZER_on();
LCD_print("ERROR!");
_delay_ms(500);
BUZZER_off();
}
} else {
// DHT22 not responding
LCD_set_cursor(0, 0);
LCD_print("Cannot Read the Temperature!"); // DHT22 error
}
_delay_ms(500);
// Read water weight from load cell
float water_weight = HX711_get_weight(WATER_HX711_DT_PIN, WATER_HX711_SCK_PIN, water_offset, water_scale);
LCD_set_cursor(1, 0);
snprintf(lcd_buffer, sizeof(lcd_buffer), "Water: %.1f g", water_weight);
LCD_print(lcd_buffer);
// Read food weight from load cell
float food_weight = HX711_get_weight(FOOD_HX711_DT_PIN, FOOD_HX711_SCK_PIN, food_offset, food_scale);
LCD_set_cursor(2, 0);
snprintf(lcd_buffer, sizeof(lcd_buffer), "Food: %.1f g", food_weight);
LCD_print(lcd_buffer);
// Check keypad input for expected food amount
char key = KEYPAD_getkey();
if (key >= '0' && key <= '9') {
expected_food_amount = (expected_food_amount * 10) + (key - '0');
snprintf(lcd_buffer, sizeof(lcd_buffer), "Expected: %.1f g", expected_food_amount);
LCD_set_cursor(3, 0);
LCD_print(lcd_buffer);
}
// Feeding mechanism control
if (food_weight < expected_food_amount) {
if (!buzzer_flag) {
BUZZER_on(); // turn on the buzzer
buzzer_flag = 1; // set the flag to indicate buzzer activation
}
LED_on(); // turn on LED
_delay_ms(100);
LED_off();
// open the feeder (servo) -> 90 degrees
SERVO_set_position(90);
LCD_set_cursor(3, 0);
LCD_print("Opened the Feeder");
_delay_ms(500); // Wait for 500ms
// check the food weight again to see if its reach expected amount or not
food_weight = HX711_get_weight(FOOD_HX711_DT_PIN, FOOD_HX711_SCK_PIN, food_offset, food_scale);
} else {
// reset the buzzer and LED flag when weight is not less than expected amount
// close the feeder (servo) -> 0 degrees
SERVO_set_position(0);
LCD_set_cursor(3, 0);
LCD_print("Closed the Feeder");
_delay_ms(500); // wait for 500ms
buzzer_flag = 0;
BUZZER_off(); //turn off the buzzer
}
_delay_ms(500);
}
return 0;
}
*/