Temperature-Controlled Fan System

ATmega328P-based ventilation system with LM35, PWM fan control, and adjustable temperature limits.

BeginnerFull instructions provided39
Temperature-Controlled Fan System

Things used in this project

Story

Read more

Schematics

Temperature-Controller Fan

Code

Tempereture-Controller Fan Code

C/C++
#include <Arduino.h>      // Wajib untuk fungsi millis() yang diizinkan 
#include <stdlib.h>       // Wajib untuk fungsi konversi data itoa()
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

// --- DEFINISI VARIABEL KONTROL ---
volatile int thresholdTemp = 25; // Nilai awal TH seperti di simulasi LCD (25)
volatile uint8_t pwmValue = 100; // Nilai awal PWM seperti di simulasi LCD (100)
float currentTemp = 0.0;

// Variabel Debounce untuk Tombol Interrupt
volatile unsigned long lastTimeUp = 0;
volatile unsigned long lastTimeDown = 0;
const unsigned long debounceDelay = 200; // 200 ms

// --- FUNGSI LOW-LEVEL LCD (MANIPULASI REGISTER PORTB) ---
void lcd_pulse_enable() {
    PORTB |= (1 << PB1);  // Set Enable High
    _delay_us(1);         // Delay pulsa singkat
    PORTB &= ~(1 << PB1); // Set Enable Low
    _delay_us(100);       // Delay eksekusi perintah
}

void lcd_send_nibble(uint8_t nibble) {
    // Bersihkan bit data PB2-PB5 tanpa mengganggu PB0 dan PB1
    PORTB &= 0xC3; 
    // Masukkan 4 bit data ke PB2-PB5 (geser kiri 2 kali)
    PORTB |= (nibble & 0x0F) << 2;
    lcd_pulse_enable();
}

void lcd_send_command(uint8_t cmd) {
    PORTB &= ~(1 << PB0); // RS = 0 (Mode Perintah)
    lcd_send_nibble(cmd >> 4);   // Kirim 4 bit tinggi (MSB)
    lcd_send_nibble(cmd);        // Kirim 4 bit rendah (LSB)
    _delay_ms(2);
}

void lcd_send_data(uint8_t data) {
    PORTB |= (1 << PB0);  // RS = 1 (Mode Data)
    lcd_send_nibble(data >> 4);   // Kirim 4 bit tinggi (MSB)
    lcd_send_nibble(data);        // Kirim 4 bit rendah (LSB)
    _delay_us(50);
}

void lcd_init() {
    // Set PB0-PB5 sebagai OUTPUT
    DDRB |= 0x3F; 
    PORTB = 0x00;

    _delay_ms(20); // Tunggu LCD siap setelah power-on

    // Prosedur inisialisasi mode 4-bit sesuai datasheet HD44780
    lcd_send_nibble(0x03);
    _delay_ms(5);
    lcd_send_nibble(0x03);
    _delay_us(150);
    lcd_send_nibble(0x03);
    lcd_send_nibble(0x02); // Set ke mode 4-bit

    // Pengaturan fungsi LCD
    lcd_send_command(0x28); // 4-bit mode, 2 baris, font 5x8
    lcd_send_command(0x0C); // Display ON, Cursor OFF, Blink OFF
    lcd_send_command(0x06); // Entry mode: Inkremen otomatis
    lcd_send_command(0x01); // Bersihkan layar LCD
    _delay_ms(2);
}

void lcd_set_cursor(uint8_t col, uint8_t row) {
    uint8_t address = (row == 0) ? (0x80 + col) : (0xC0 + col);
    lcd_send_command(address);
}

void lcd_print(const char* str) {
    while (*str) {
        lcd_send_data(*str++);
    }
}

void lcd_print_int(int val) {
    char buf[6];
    itoa(val, buf, 10); // Sekarang aman karena ada <stdlib.h>
    lcd_print(buf);
}

void lcd_print_float(float val) {
    int integerPart = (int)val;
    int decimalPart = (int)((val - integerPart) * 10);
    if(decimalPart < 0) decimalPart = -decimalPart;

    lcd_print_int(integerPart);
    lcd_send_data('.');
    lcd_print_int(decimalPart);
}

// --- FUNGSI UTAMAKAN INISIALISASI PERIPHERAL ---
void init_adc() {
    // REFS0 = 1 (AVCC), MUX0-3 = 0000 (Channel ADC0/PC0)
    ADMUX = (1 << REFS0);
    // ADEN = 1 (Aktifkan ADC), Prescaler 128
    ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
}

uint16_t read_adc() {
    ADCSRA |= (1 << ADSC); // Mulai konversi
    while (ADCSRA & (1 << ADSC)); // Tunggu selesai
    return ADC;
}

void init_pwm_timer0() {
    // Set PD4, PD5, PD6 sebagai OUTPUT untuk Driver L298
    DDRD |= (1 << DDD4) | (1 << DDD5) | (1 << DDD6);

    // Konfigurasi arah motor awal (IN1=HIGH, IN2=LOW)
    PORTD |= (1 << PORTD4);
    PORTD &= ~(1 << PORTD5);

    // Fast PWM Mode, Non-Inverting Mode pada OC0A (PD6)
    TCCR0A = (1 << COM0A1) | (1 << WGM01) | (1 << WGM00);
    TCCR0B = (1 << CS01) | (1 << CS00); // Prescaler 64

    OCR0A = pwmValue; 
}

void init_interrupts() {
    DDRD &= ~((1 << DDD2) | (1 << DDD3)); // Set PD2 & PD3 sebagai INPUT

    // Trigger INT0 & INT1 pada kondisi RISING Edge
    EICRA = (1 << ISC11) | (1 << ISC10) | (1 << ISC01) | (1 << ISC00);
    EIMSK = (1 << INT1) | (1 << INT0); // Aktifkan interupsi eksternal
}

// --- INTERRUPT SERVICE ROUTINE (ISR) ---
ISR(INT0_vect) {
    unsigned long currentTime = millis(); // Sekarang aman karena ada <Arduino.h>
    if (currentTime - lastTimeUp > debounceDelay) {
        thresholdTemp++;
        if (thresholdTemp > 60) thresholdTemp = 60;
        lastTimeUp = currentTime;
    }
}

ISR(INT1_vect) {
    unsigned long currentTime = millis(); // Sekarang aman karena ada <Arduino.h>
    if (currentTime - lastTimeDown > debounceDelay) {
        thresholdTemp--;
        if (thresholdTemp < 15) thresholdTemp = 15;
        lastTimeDown = currentTime;
    }
}

// --- ARDUINO FRAMEWORK STRUCTURE ---
void setup() {
    lcd_init();
    init_adc();
    init_pwm_timer0();
    init_interrupts();
    
    sei(); // Aktifkan interupsi global
}

void loop() {
    // 1. Membaca Suhu dari LM35 via Register ADC
    uint16_t adcResult = read_adc();
    currentTemp = (adcResult * 5.0 * 100.0) / 1024.0;

    // 2. Logika Penentuan Kecepatan Kipas (PWM)
    if (currentTemp < thresholdTemp) {
        pwmValue = 0;
    } else {
        int tempDiff = (int)currentTemp - thresholdTemp;
        int calculatedPwm = 100 + (tempDiff * 15); 
        
        if (calculatedPwm > 255) calculatedPwm = 255;
        if (calculatedPwm < 100) calculatedPwm = 100;
        
        pwmValue = (uint8_t)calculatedPwm;
    }

    // Update Nilai Register PWM Kipas (OC0A / PD6)
    OCR0A = pwmValue;

    // 3. Update Informasi Tampilan di LCD
    lcd_set_cursor(0, 0);
    lcd_print("T: ");
    lcd_print_float(currentTemp);
    lcd_send_data(0xDF); 
    lcd_print("C   ");

    lcd_set_cursor(0, 1);
    lcd_print("TH:");
    lcd_print_int(thresholdTemp);
    lcd_print("  ");
    
    lcd_set_cursor(8, 1);
    lcd_print("PWM:");
    lcd_print_int(pwmValue);
    lcd_print("   ");

    _delay_ms(100); 
}

Credits

Wulan dari
1 project • 0 followers
Tamara Prazenda
1 project • 0 followers
Gio Vani
1 project • 0 followers
Charlypetrus Sihite
1 project • 0 followers
Rudy
1 project • 0 followers
Risky Akbar
1 project • 0 followers

Comments