Home IoT Hub Series · 19

[19편] 사람 있을 때만 시원하게, 없을 땐 아끼자 - PIR 기반 자동 에어컨 제어

[19편] 사람 있을 때만 시원하게, 없을 땐 아끼자 - PIR 기반 자동 에어컨 제어 대표 이미지
시스템 아키텍처 및 데이터 파이프라인 안내
이 글은 2편: 두뇌 만들기에서 구축한 중앙 NodeMCU 서버에 연결되는 우리집 IoT 허브 구성 문서입니다. PIR·DHT22·IR 제어를 결합해 사람이 있을 때만 냉방 판단을 수행하는 자동 제어 노드입니다. 수집된 데이터는 로컬 무선망 IoT_Hub_Net을 통해 중앙 서버의 1번 실내 온도와 4번 에어컨 상태 슬롯(/set?no1=온도&no4=상태)을 갱신하며, 20채널 스마트홈 통합 컨트롤러의 판단 근거로 사용됩니다.

17편에서는 리모컨 신호를 학습해서 수동으로 에어컨을 켜고 끄는 것까지 했다. 이번에는 한 걸음 더 나아간다. 사람이 있는지 감지해서, 더울 때만 자동으로 에어컨을 켜고, 사람이 없으면 알아서 끄는 진짜 스마트 제어다.

3편의 거실 D1 Mini에 PIR 센서 하나만 추가하면 됩니다. 이미 DHT22로 온도를 측정하고 있으므로, 여기에 재실 여부를 더하면 조건 판단이 가능해집니다.

준비물 (3편에서 추가되는 것만)

부품수량개당 가격
HC-SR501 PIR 센서1개1,000원
점퍼 와이어 3개
추가 합계1,000원

기존 거실 D1 Mini + DHT22는 이미 설치되어 있다고 가정합니다.

핀 연결 (기존 배선에 추가)

[19편] 사람 있을 때만 시원하게, 없을 땐 아끼자 - PIR 기반 자동 에어컨 제어 배선도
센서/모듈보드 연결
VCC5V (VU)
GNDGND
OUTD5 (GPIO14)
VCC3.3V
DATAD1 (GPIO5)
GNDGND
PIR은 5V 전원이 필요하다. D1 Mini의 VU 핀(USB 5V)을 쓴다. DHT22는 그대로 3.3V.

동작 로직

D1 Mini가 혼자 판단한다. 두뇌를 거치지 않고 자체 로직으로 AC ON/OFF를 결정한 뒤, 두뇌에 4번 슬롯 값을 덮어쓴다.

자동 냉방 판단 흐름

재실 감지PIR이 거실에 사람이 있는지 확인
온도 조건DHT22 온도가 26℃보다 높은지 확인
자동 ON두 조건이 모두 맞으면 4번 슬롯에 AC ON 전송
자동 OFF5분 이상 움직임이 없거나 24℃ 아래로 내려가면 AC OFF 전송

사람이 없거나(5분 이상 PIR 무반응) 온도가 내려가면(24℃ 이하) AC OFF를 보냅니다. ON/OFF가 너무 자주 바뀌지 않도록 5분 쿨다운을 둡니다.

전체 코드

3편 코드를 덮어씌운다. Wi-Fi, DHT22 부분은 동일하고 PIR 읽기 + 조건 판단 + AC 제어가 추가됐다.

[19편] 사람 있을 때만 시원하게, 없을 땐 아끼자 - PIR 기반 자동 에어컨 제어 - 전체 코드
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <DHT.h>

const char* ssid     = "IoT_Hub_Net";
const char* password = "iothub1234";
const char* hubURL   = "http://192.168.1.1";

#define DHTPIN     5   // D1
#define DHTTYPE    DHT22
#define PIR_PIN    14  // D5
#define TEMP_ON    26.0  // 26℃ 이상이면 에어컨 켜기
#define TEMP_OFF   24.0  // 24℃ 이하이면 에어컨 끄기
#define PIR_TIMEOUT 300000 // 5분 동안 움직임 없으면 사람 없는 것으로 판정
#define COOLDOWN   300000 // AC ON/OFF 전환 후 5분 쿨다운
#define INTERVAL   5000

DHT dht(DHTPIN, DHTTYPE);

bool acOn = false;
unsigned long lastMotion = 0;
unsigned long lastAcToggle = 0;

void setup() {
  Serial.begin(115200);
  dht.begin();
  pinMode(PIR_PIN, INPUT);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to IoT_Hub_Net");
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println("\nConnected! Smart AC ready.");
}

void loop() {
  // ── 센서 읽기 ──
  float temp = dht.readTemperature();
  float hum  = dht.readHumidity();
  bool motion = digitalRead(PIR_PIN); // HIGH=움직임 감지

  if (isnan(temp) || isnan(hum)) {
    Serial.println("DHT22 read failed");
    delay(2000); return;
  }

  // 움직임이 감지되면 타이머 리셋
  if (motion) lastMotion = millis();

  // ── 자동 판단 ──
  unsigned long now = millis();
  bool occupied = (now - lastMotion < PIR_TIMEOUT); // 최근 5분 내 움직임 = 재실
  bool tooHot = (temp > TEMP_ON);
  bool coolEnough = (temp < TEMP_OFF);

  // 쿨다운 중이면 AC 변경 안 함
  if (now - lastAcToggle < COOLDOWN) {
    // 쿨다운 중에도 온습도는 계속 보고
  } else {
    if (occupied && tooHot && !acOn) {
      // 조건 충족 → AC ON
      acOn = true;
      lastAcToggle = now;
      sendAcCommand("ON");
      Serial.println(">>> AC AUTO ON (hot + occupied)");
    } else if ((!occupied || coolEnough) && acOn) {
      // 사람 없거나 충분히 시원 → AC OFF
      acOn = false;
      lastAcToggle = now;
      sendAcCommand("OFF");
      Serial.println(">>> AC AUTO OFF (empty or cool enough)");
    }
  }

  // ── 정기 온습도 보고 (항상) ──
  String state = acOn ? "ON" : "OFF";
  Serial.printf("Temp: %.1f C, Hum: %.1f %%, Motion: %s, AC: %s\n",
                temp, hum, motion ? "YES" : "no", state.c_str());

  WiFiClient client; HTTPClient http;
  String url = String(hubURL) + "/set?no1=" + String(temp)
             + "&no2=" + String(hum);
  // AC 상태도 함께 보내되, AC는 별도로 이미 sendAcCommand로 제어했으므로
  // 여기서는 현재 AC 상태 동기화용으로만 보낸다
  url = url + "&no4=" + state;
  http.begin(client, url);
  int code = http.GET(); http.end();

  Serial.print("HTTP "); Serial.print(code);
  Serial.print(" -> "); Serial.println(url);
  Serial.println();

  delay(INTERVAL);
}

// ── AC ON/OFF 전송 (별도 요청으로 확실하게) ──
void sendAcCommand(String cmd) {
  WiFiClient client; HTTPClient http;
  String url = String(hubURL) + "/set?no4=" + cmd;
  http.begin(client, url);
  http.GET(); http.end();
  Serial.println("AC COMMAND -> " + url);
}

업로드 및 확인

USB 데이터 케이블로 Wemos D1 Mini를 PC에 연결하고, Arduino IDE에서 ESP8266 보드와 포트를 선택한 뒤 소스를 업로드합니다. 업로드가 끝나면 툴 → 시리얼 모니터를 열고 보드레이트를 115200으로 맞춥니다.

시리얼 모니터

업로드가 끝나면 Arduino IDE의 툴 → 시리얼 모니터를 열고 보드레이트를 115200으로 맞춥니다. 다음은 정상 동작을 확인하기 위한 시리얼 모니터 출력 예시입니다. 이 로그는 실제 캡처가 아니라, 사람 감지·온도·에어컨 상태를 묶어 자동 제어 결정을 내리는 흐름 예시입니다.

[BOOT] Home IoT Node #19 - Auto AC controller
[INPUT] room_temp(no1)=28.4C presence=YES ac_state(no4)=OFF
[RULE] temp_high=true, person_present=true, cooldown_guard=PASS
[COMMAND] AC_ON via IR
[HTTP] GET /set?no4=1
[HUB] 200 OK - slot no4=AC_ON
[LOOP] next decision in 60000 ms
  • Connected 또는 connected가 보이면 Wi-Fi 연결 단계는 통과한 것입니다.
  • HTTP 200 또는 200 OK가 보이면 두뇌 서버에 값이 전달된 것입니다.
  • 값이 갱신되지 않으면 먼저 URL의 슬롯 번호와 센서 배선을 함께 확인합니다.

PIR 타이머 조정

HC-SR501 모듈에는 주황색 가변저항 두 개가 있다.

  • 왼쪽 (감도): 시계 방향으로 돌리면 감지 거리가 늘어난다 (최대 7m). 거실 크기에 맞게.
  • 오른쪽 (타임): 감지 후 HIGH를 유지하는 시간. 반시계 방향 끝까지 돌려서 최소(약 3초)로 둔다. 타이머는 코드에서 관리하니까.

점퍼 핀을 H 위치(Retriggering)로 두면 움직임이 계속되는 동안 HIGH가 유지됩니다.

확인

1. 거실에 사람이 없고 26℃ 미만이면 AC는 OFF 상태. 2. 사람이 들어오고 온도가 26℃를 넘으면 AC가 자동으로 ON. 3. 사람이 나가서 5분간 움직임이 없으면 AC OFF. 4. 사람이 있어도 온도가 24℃ 이하로 내려가면 AC OFF.

시리얼 모니터에 >>> AC AUTO ON (hot + occupied) 또는 >>> AC AUTO OFF (empty or cool enough) 메시지가 표시되는지 확인해 동작을 검증합니다.

안 될 때 체크포인트

증상원인해결
AC가 아예 안 켜짐PIR 감도가 너무 낮음왼쪽 가변저항을 시계 방향으로 돌려 감도를 올립니다.
AC가 너무 자주 ON/OFF 반복쿨다운이 짧음COOLDOWN 값을 600000(10분)으로 늘립니다.
사람이 가만히 앉아 있으면 OFF됨PIR 감지 타임이 짧음오른쪽 가변저항을 시계 방향으로 약간 돌려 5초 정도로 설정합니다. 코드의 PIR_TIMEOUT도 600000(10분)으로 늘립니다.
AC ON을 보냈는데 에어컨 반응 없음IR 학습 미완료17편으로 돌아가서 리모컨 코드 학습이 제대로 되었는지 확인합니다. AC_CODE가 실제 리모컨 코드와 일치해야 합니다.

현장 추가 체크포인트

이 노드는 코드만 보면 짧아 보일 수 있지만, 실제 집에 붙이면 센서 위치·전원·무선망·노이즈 때문에 값이 쉽게 흔들립니다. 아래 항목은 두뇌 서버에 잘못된 판단 근거가 들어가지 않도록 현장에서 먼저 점검할 내용입니다.

증상가능한 원인현장 해결 방법
사람이 있는데도 에어컨이 꺼짐PIR 센서는 정지한 사람을 오래 감지하지 못함마지막 움직임 후 10~20분 유지 시간을 두고, 온도 조건만으로 즉시 OFF하지 않게 합니다.
커튼이나 반려동물에 반응함PIR 시야각이 넓고 열원 움직임을 사람으로 오인함센서 각도를 사람 동선 중심으로 낮추고, 짧은 감지는 무시하는 시간 필터를 둡니다.
냉방이 켜졌다 꺼졌다 반복됨온도 임계값 하나만 사용해 경계값 근처에서 플래핑 발생켜짐 28℃, 꺼짐 26℃처럼 히스테리시스를 적용합니다.
IR 명령은 보냈는데 냉방이 안 됨에어컨 수신 실패 또는 현재 모드와 명령 상태 불일치IR 송신 후 토출구 온도 변화나 4번 슬롯 상태를 일정 시간 뒤 확인하는 피드백 루틴을 둡니다.

지금까지 구성한 흐름

  • 3편 거실 센서 노드에 PIR 하나를 추가했다.
  • D1 Mini가 온도와 재실 여부를 판단해 에어컨을 자동으로 켜고 끕니다.
  • 두뇌를 거친 IR 송출로 실제 에어컨 제어까지 완성.
  • 이제 여름에 집에 들어오면 에어컨이 자동으로 켜지고, 나가면 자동으로 꺼지는 구조가 됩니다.
우리집 IoT HUB 20슬롯 시스템 맵

이 글은 2편의 중앙 두뇌 서버와 3~19편의 분산 엣지 노드가 함께 구성하는 20채널 홈 IoT 시스템의 일부입니다. 필요한 편으로 이동해 슬롯과 센서 흐름을 함께 확인합니다.

[1편] 우리집 IoT 허브 전체 그림[2편] 중앙 두뇌 서버 만들기 · 슬롯 1~20[3편] 집 안 온습도, 감으로 보지 말고 숫자로 보자 · 슬롯 1·2[4편] 문 닫았나 계속 신경 쓰일 때 · 슬롯 9[5편] 냉장고가 제대로 돌고 있는지 눈으로 확인하자 · 슬롯 12[6편] 창문 열어도 될까? · 슬롯 3[7편] 욕실 곰팡이 생기기 전에 습도부터 잡자 · 슬롯 19[8편] 보일러가 실제로 도는지 배관 온도로 확인하자 · 슬롯 5·17[9편] 외출했는데 가스레인지가 켜져 있지 않을까? · 슬롯 6[10편] 가스 냄새가 나기 전에 먼저 경고받자 · 슬롯 7[11편] 집을 비웠을 때 불꽃 징후를 먼저 잡자 · 슬롯 8[12편] 머리가 멍한 이유가 공기 때문인지 확인하자 · 슬롯 13·14[13편] 전기세 무지 나오네, 이번 기회에 눈으로 보자 · 슬롯 15[14편] 물 새는지, 얼마나 쓰는지 숫자로 보자 · 슬롯 16[15편] 비 오는 줄 모르고 창문 열어두지 않게 · 슬롯 18[16편] 불 껐나, 문 잠갔나 한 번에 확인하자 · 슬롯 11·10[17편] 리모컨 없이 에어컨을 IoT 허브에서 제어하자 · 슬롯 4[18편] 강아지 밥그릇이 비었는지 눈으로 보자 · 슬롯 20[19편] 사람 있을 때만 시원하게, 없을 땐 아끼자 · 슬롯 1·4[20편] 집 안 상태가 한 화면에 보이기 시작했다 · 슬롯 전체