SG90+DHT11

김준혁·4일 전

습도가 20초과이면 서보모터가 열림
미만이면 닫힘 상태로 작동하고, 시리얼 모니터에 현재 창문이 열렸는지와 온/습도 출력 프로그램.
9번(SG90),2번(DHT11)핀 사용

Arduino

#include <Servo.h>
#include <DHT.h>

// [핀 설정]
#define DHTPIN 2       // DHT11 데이터 핀
#define DHTTYPE DHT11  // 센서 타입 설정
#define SERVO_PIN 9    // 서보 모터 핀

// [객체 생성]
DHT dht(DHTPIN, DHTTYPE);
Servo myServo;

// [상태 관리 변수]
unsigned long lastSensorRead = 0;
const unsigned long interval = 2000; // 2초 간격

void setup() {
  Serial.begin(9600);
  dht.begin();
  myServo.attach(SERVO_PIN);
  
  myServo.write(0); // 초기 상태: 닫힘
  Serial.println("--- Arduino Intelligent Window System Start ---");
}

void loop() {
  // [논블로킹 타이머] millis()를 사용해 2초마다 실행
  unsigned long currentMillis = millis();

  if (currentMillis - lastSensorRead >= interval) {
    lastSensorRead = currentMillis;

    // 데이터 읽기
    float hum = dht.readHumidity();
    float temp = dht.readTemperature();

    // 센서 오류 체크
    if (isnan(hum) || isnan(temp)) {
      Serial.println("Failed to read from DHT sensor!");
      return;
    }

    // 시리얼 모니터 출력
    Serial.print("Temp: "); Serial.print(temp);
    Serial.print("C, Hum: "); Serial.print(hum);
    Serial.print("% | Window: ");

    // --- 히스테리시스 제어 로직 ---
    
    if (hum > 20) {
      myServo.write(90); // 90도로 이동
      Serial.println("OPEN");
    }
    
    else if (hum < 20) {
      myServo.write(0); // 0도로 이동
      Serial.println("CLOSED");
    }
    // 이전 각도 유지
    else {
      Serial.println("KEEPING STATE");
    }
  }

  // 루프가 멈추지 않으므로 여기에 다른 실시간 코드 추가 가능!
}

AVR

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "UART0.h"  // 기존에 사용하던 UART 라이브러리 포함

// [핀 정의]
#define DHT11_PIN     PD2    // DHT11 데이터 (디지털 2번)
#define SERVO_PIN     PB1    // SG90 신호 (디지털 9번)

// [서보 각도 설정]
#define WINDOW_OPEN   3000   // 90도 (약 1.5ms)
#define WINDOW_CLOSE  1000   // 0도 (약 0.5ms)

volatile uint32_t ms_ticks = 0;

// --- [1] Timer 0: 1ms 시스템 틱 (논블로킹 시간 측정) ---
void Timer0_Init(void) {
    TCCR0A = (1 << WGM01);              // CTC 모드
    TCCR0B = (1 << CS01) | (1 << CS00); // 분주비 64
    OCR0A = 249;                        
    TIMSK0 |= (1 << OCIE0A);            
}

// --- [2] Timer 1: 서보 하드웨어 PWM ---
void Timer1_Init(void) {
    DDRB |= (1 << SERVO_PIN);           
    TCCR1A = (1 << COM1A1) | (1 << WGM11);
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); 
    ICR1 = 39999;                       
    OCR1A = WINDOW_CLOSE;               
}

// --- [3] DHT11: 직접 제어 함수 ---
uint8_t DHT11_read_byte(void) {
    uint8_t result = 0;
    for (int i = 0; i < 8; i++) {
        while (!(PIND & (1 << DHT11_PIN))); 
        _delay_us(30);                      
        if (PIND & (1 << DHT11_PIN)) result |= (1 << (7 - i));
        while (PIND & (1 << DHT11_PIN));    
    }
    return result;
}

int8_t DHT11_get_data(uint8_t *h, uint8_t *t) {
    uint8_t data[5];
    DDRD |= (1 << DHT11_PIN);
    PORTD &= ~(1 << DHT11_PIN);
    _delay_ms(18);
    PORTD |= (1 << DHT11_PIN);
    _delay_us(30);
    DDRD &= ~(1 << DHT11_PIN); 

    _delay_us(40);
    if (PIND & (1 << DHT11_PIN)) return -1;
    _delay_us(80);
    if (!(PIND & (1 << DHT11_PIN))) return -1;
    _delay_us(80);

    for (int i = 0; i < 5; i++) data[i] = DHT11_read_byte();

    if (data[4] == (data[0] + data[1] + data[2] + data[3])) {
        *h = data[0]; 
        *t = data[2]; 
        return 0;
    }
    return -2;
}

ISR(TIMER0_COMPA_vect) {
    ms_ticks++;
}

// --- [4] 메인 루프 ---
int main(void) {
    Timer0_Init();
    Timer1_Init();
    UART0_init(); // UART 초기화 (9600bps 가정)
    sei(); 

    uint8_t hum = 0, temp = 0;
    uint32_t last_sensor_read = 0;

    UART0_print_string("--- Intelligent Ventilation System Start ---\r\n");

    while (1) {
        // [논블로킹] 2초마다 센서 감시 및 시리얼 출력
        if (ms_ticks - last_sensor_read >= 2000) {
            last_sensor_read = ms_ticks;

            if (DHT11_get_data(&hum, &temp) == 0) {
                // 1. 시리얼 모니터로 현재 상태 전송
                UART0_print_string("Temp: ");
                UART0_print_1_byte_number(temp);
                UART0_print_string("C, Hum: ");
                UART0_print_1_byte_number(hum);
                UART0_print_string("% | Window: ");

                // 2. 히스테리시스 제어 로직 및 창문 상태 출력
                if ( hum > 20) {
                    OCR1A = WINDOW_OPEN;
                    UART0_print_string("OPEN\r\n");
                }
                else if (hum < 20) {
                    OCR1A = WINDOW_CLOSE;
                    UART0_print_string("CLOSED\r\n");
                }
                else {
                    UART0_print_string("KEEPING STATE\r\n");
                }
            } else {
                UART0_print_string("Sensor Read Error!\r\n");
            }
        }
        
        // 메인 루프는 계속 회전하므로 다른 실시간 작업 추가 가능
    }
}
#include "UART0.h"


void UART0_init(void)
{
    UBRR0H = 0x00;
    UBRR0L = 207;           // 9,600 보율 설정
    UCSR0A |= _BV(U2X0);    // 2배속 모드
    UCSR0C = 0x06;          // 비동기, 8비트 데이터, 패리티 없음, 1비트 정지 비트
    UCSR0B |= _BV(RXEN0);   // 수신 가능
    UCSR0B |= _BV(TXEN0);   // 송신 가능
}

void UART0_transmit(char data)
{
    while(!(UCSR0A & (1<<UDRE0))); // 송신 대기
    UDR0 = data;
}

unsigned char UART0_receive(void)
{
    while(!(UCSR0A & (1<<RXC0))); // 수신 대기
    return UDR0;
}

void UART0_print_string(char *str)
{
    for(int i = 0; str[i]; i++)
        UART0_transmit(str[i]);
}

void UART0_print_1_byte_number(uint8_t n)
{
    char numString[4] = "0";
    int i, index = 0;
    if(n > 0) {
        for(i = 0; n != 0; i++) {
            numString[i] = n % 10 + '0';
            n = n / 10;
        }
        numString[i] = '\0';
        index = i - 1;
    }
    for(i = index; i >= 0; i--)
        UART0_transmit(numString[i]);
}

#ifndef UART0_H_
#define UART0_H_

#include <avr/io.h>

void UART0_init(void);
void UART0_transmit(char data);
unsigned char UART1_receive(void);
void UART0_print_string(char *str);
void UART0_print_1_byte_number(uint8_t n);


#endif /* UART0_H_ */
profile
임베디드 개발자

0개의 댓글