본문 바로가기
아두이노/냉난방기 원격 조절기

#1 기획 및 검토

by songbum 2024. 11. 12.

사무실이 단열이 잘 안 돼 있다보니 겨울에는 보일러나 수도가 얼어터지지 않을까 걱정이 많아진다.  다행히 아직까지는 직접 겪어보지는 않았지만, 전에 근무하던 직장의 아파트형 공장에서 겨울에 수도가 터져 베란다로 물이 흐르는 사례를 몇 번 목격 했었다.  현재 사무실 난방은 가스보일러를 사용하고 있는데, 사무실을 비운 사이에 온도가 급감하는 경우(특히 새벽)에는 좀 위험할 거 같다.  그래서 작년에는 사람이 있건 없건 항상 실내온도를 높여 놨었는데, 빈 방에 난방을 하자니 좀 낭비하는 거 같아서 해결 방법을 찾아봤다.  

 

보일러 컨트롤 패널을 웹캠으로 모니터링 해서 실내 온도가 너무 낮다 싶으면 원격으로 보일러 스위치를 켜서 난방을 시작하고, 어느 정도 시간이 지나면 스위치를 꺼서 난방을 중지시키는 원격 리모컨을 아두이노로 개발하기로 했다.  필요한 기술들은 모두 전에 이미 사용해 본 것들이라 쉽게 제작 가능해보였다.

 

필요한 주요 구성품은 아두이노 Nano 보드, 와이파이 모듈 ESP-01, 서보 모터 SG90 2개이다.  이 서보 모터 2개로 컨트롤 패널에 있는 온도 +, 온도 -, 자동, 외출 버튼 총 4개를 누르도록 하면 된다.

 

1. 보일러 온도조절기에 부착할 케이스 제작

2. 케이스에 서보 모터 장착하고, 보일러 온도조절기 버튼 눌러지도록 고정

3. 와이파이 연결

4. 제어용 웹페이지 생성해서 버튼 클릭 UI 구성

 

 

 

서보모터가 버튼을 정확히 누를 수 있도록 자리를 고정해주는 브라켓을 프라판으로 제작해 보일러 컨트롤러에 고정해주도록 했다.  다소 허술해보이긴 하나 조만간 사무실을 이전할 생각이라 최대한 보일러 컨트롤러를 훼손하지 않는 방향으로 작업을 했다.

 

 

 

아래와 같이 아두이노 회로를 구성한 후 테스트를 진행했다.

 

 

 

 

아두이노 IDE 에서 Arduino Nano 보드로 설정한 후 아래 프로그램을 업로드했다.

#include "Servo.h"
#include "WiFiEsp.h"
#include "SoftwareSerial.h"

SoftwareSerial mySerial(10, 11); // RX, TX

char ssid[] = "{와이파이 SSID}";            // your network SSID (name)
char pass[] = "{와이파이 비밀번호}";        // your network password
int status = WL_IDLE_STATUS;     // the Wifi radio's status

WiFiEspServer server(80);

RingBuffer buf(20);

int SERVO_1_PIN = 3;
int SERVO_2_PIN = 4;

Servo myservo_1;
Servo myservo_2;

void setup() {
  Serial.begin(9600);
  while(!Serial && millis()<5000) {
  }

  mySerial.begin(9600);
  while(!mySerial && millis()<5000) {
  }

  WiFi.init(&mySerial);

  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    while (true);
  }

  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass);
  }

  printWifiStatus();

  server.begin();

  Serial.println("START");
}

void loop() {
  int type = 404;
  String page = "";

  WiFiEspClient client = server.available();
  if (client) {
    buf.init();

    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        buf.push(c);
        //Serial.write(c);
       
        if (buf.endsWith("GET /console.html")) {
          Serial.println("console.html");
          myservo_1.attach(SERVO_1_PIN);
          delay(100);
          myservo_1.write(30);
          delay(100);
          myservo_2.attach(SERVO_2_PIN);
          delay(100);
          myservo_2.write(30);
          delay(100);
          myservo_2.detach();
          sendHttpResponse(client, 200, "console.html");
          break;
        }
        else if (buf.endsWith("GET /temp_up.html")) {
         
Serial.println("temp_up.html");
          myservo_2.attach(SERVO_2_PIN);
          delay(100);
          myservo_2.write(0);
          delay(100);
          myservo_2.write(30);
          delay(100);
          myservo_2.detach();
          sendHttpResponse(client, 200, "temp_up.html");
          break;
        }
        else if (buf.endsWith("GET /temp_down.html")) {
          Serial.println("temp_down.html");
          myservo_2.attach(SERVO_2_PIN);
          delay(100);
          myservo_2.write(60);
          delay(100);
          myservo_2.write(30);
          delay(100);
          myservo_2.detach();
          sendHttpResponse(client, 200, "temp_down.html");
          break;
        }
        else if (buf.endsWith("GET /auto.html")) {          
          Serial.println("auto1.html");
          myservo_2.attach(SERVO_2_PIN);
          delay(100);
          myservo_2.write(0);
          delay(100);
          myservo_2.write(30);
          delay(100);
          myservo_2.detach();
          sendHttpResponse(client, 200, "auto.html");
          break;
        }
        else if (buf.endsWith("GET /away.html")) {
          Serial.println("away1.html");
          myservo_2.attach(SERVO_2_PIN);
          delay(100);
          myservo_2.write(60);
          delay(100);
          myservo_2.write(30);
          delay(100);
          myservo_2.detach();
          sendHttpResponse(client, 200, "away.html");
          break;
        }
      }
    }
    delay(100);
    client.stop();
  }
}

void printWifiStatus() {
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
 
  Serial.println();
  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);
  Serial.println();
}

void sendHttpResponse(WiFiEspClient client, int type, String page) {
  if (type == 404) {
    client.print(
      "HTTP/1.1 404 Not Found\r\n"
      "Content-Type: text/html\r\n"
      "Connection: close\r\n"
      "\r\n");
  }
  else {
    Serial.print(type);
    if (page == "console.html") {
      client.print(
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/html\r\n"
        "Connection: close\r\n"
        "\r\n");
      client.print("<!DOCTYPE HTML>\r\n");
      client.print("<html>\r\n");
      client.print("<h1>Hello</h1>\r\n");
      client.print("It's RC boiler controller.\r\n");
      client.print("<script>");
      client.print("document.write(new Date());");
      client.print("</script>");
      client.print("</html>\r\n");
    }
    else {
      client.print(
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: text/html\r\n"
        "Connection: close\r\n"
        "\r\n");
      client.print("<!DOCTYPE HTML>\r\n");
      client.print("<html>\r\n");
      client.print("<h1>Hello</h1>\r\n");
      client.print("It's RC boiler controller.\r\n");
      client.print(page);
      client.print("\r\n");
      client.print("</html>\r\n");
    }
  }
}
 
서보 모터가 메이커별로 또는 제품 별로 움직이는 범위(각도)에 편차가 있기 때문에, 일단 위와 같이 업로드하고 테스트하면서 조금씩 수정해나가기로 했다.