기본 콘텐츠로 건너뛰기

Arduino Webserver + Relay + RaspberryPi3 model B + Homebridge 로 보일러컨트롤하기


https://www.blogger.com/blogger.g?blogID=4697918918837153418#editor/target=post;postID=61610120495753393;onPublishedMenu=allposts;onClosedMenu=allposts;postNum=9;src=postname


 기존에 작성했던 상기 포스트가 완료되었고, 기숙사 생활을 청산(?) 한 뒤 자취를 시작하게 되었다.
스탠드, 인체감지 센서, 온습도 센서, 조도 센서, 스탠드, 선풍기, 에어컨, TV 들은 이제 샤오미로 대체했고

 19.02.16 - 빌트인 보일러를 외부에서 끄고 켜는 것에 도전해보게 되었다. 애인도 없이 외로이 홀로 살다보니 퇴근해서 얼음장 같은 집에 들어가면 안그래도 외로운데 더 춥고 더 외롭다...
자 우리의 친구 아두이노 웹서버와 함께 미리미리 펄펄 끓는 따뜻한 겨울을 보내보도록 하자.
기계는 바람도 안피고 거짓말도 하지 않는다. 사람보다 기계가 낫다 


코딩 작업
코딩을 하기에 앞서 우선 이 컨트롤러의 기능적 특징이 있는데
1. 전원버튼/실내/ 외출/ 목욕/ 예약 모드 중 실내버튼을 1회 누르면 실내기능, 2회면 예약, 3회면 외출 기능이 된다.
2. 목욕버튼은 어떤 모드에서 한방에 목욕기능으로 전환된다.
3. (핵심) 목욕 기능 구동 중 원하는 모드로 전환하려면 목욕 기능 구동 직전 모드가 실내인지 외출인지 예약인지에 따라서 버튼을 누르는 횟수가 달라진다

아래는 위 내용을 토대로 작성한 코드이다. 대부분의 예전 웹서버 코드가 재활용되었다.


#include <Arduino.h>
#include <SPI.h>
#include <Ethernet.h>


#define in 1  //스탠드 릴레이에 신호를 보낼 핀설정
#define reserv 2  //출입문 릴레이에 신호 보낼 핀임
#define out 3
#define bath 3
byte mac[] = { 0x5E, 0x3D, 0x54, 0xF5, 0xE1, 0xD3 } ; //mac address
IPAddress ip(192, 168, 0, 240) ;
EthernetServer server(9090) ;
String readString;


int val; 
int Sval;
int Prestatus;

void setup()
{
  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.begin(115200);
  pinMode(2, OUTPUT); //  relay2,3,4,5 를 output으로 설정한다.
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  digitalWrite(2, HIGH);
  digitalWrite(3, HIGH);
  digitalWrite(4, HIGH);
  digitalWrite(5, HIGH); // 이걸 안넣었더니 보일러 컨트롤러가 시작부터 그냥 먹통됨
  val = 1;
}





void loop()
{
    
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {                     // This is all where we start up the server and strings.
        char c = client.read();
        if (readString.length() < 100) {
          readString += c;
        }
        if (c == '\n') {

          Serial.println(readString);
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html; charset=UTF-8");
          client.println("");
          client.println("<hmtl>");
          client.println("<head>");
          client.println("<meta charset=utf-8>");
          client.println("<meta name=viewport content=width=device-width, initial-scale=1>");
          client.println("</head>");
          client.println("<title>");
          client.println("예진이의 보일러 오토메이션 Page");
          client.println("</title>");
          client.println("<body bgcolor=black>");
          client.println("<font color=white>");
          client.println("<meta http-equiv=\"refresh\" content=\"4\">"); 
          client.println("<center>"); 
          client.println("<p>");
          client.println("<FORM>");
          client.println("</br>");
          client.println("</br>"); 
          client.println("</br>");
  
          client.println("<INPUT type=button value=실내 onClick=window.location='/?in'>");
          client.println("<INPUT type=button value=예약 onClick=window.location='/?reserv'>");
          client.println("<INPUT type=button value=외출 onClick=window.location='/?out'>");
          client.println("<INPUT type=button value=목욕 onClick=window.location='/?bath'>");
          client.println("<INPUT type=button value=초기화 onClick=window.location='/?reset'>");
          client.print("<table border=1 width=200>");
          client.print("<tr>");
          client.print("<td align=center>");
          client.print("<font color=white size=3>");
          client.print("<b>");
          client.print("현재 보일러 상태");
          client.print("</b>");
          client.print("</td>");
          client.print("</tr>");
          client.print("<tr>");
          client.print("<td align=center>");
          client.print("<font color=white size=3>"); 
          if (val == 1) {
            client.print("<font color=magenta size=3>");
            client.print("실내");
                        }
          if (val == 2) {
            client.print("<font color=white size=3>");
            client.print("예약");
                        }
          if (val == 3) {
            client.print("<font color=white size=3>");
            client.print("외출");
                        }
          if (val == 4) {
            client.print("<font color=yellow size=3>");
            client.print("목욕");
                        }

          
          client.print("</td>");
          client.print("</tr>");
          client.print("</table>"); 
          client.println("</br>");  
          client.print("</FORM>");      
          client.print("</center>");
          client.print("</font>");
          client.print("</body>");
          client.print("</html>");
          delay(1);
          
          
          if (readString.indexOf("?in") > 0)
             {
              
              if (val == 1){ // val이 현 1로 실내 상태일 경우에는 아무것도 안함
                            
                               val = 1;
                               Prestatus = 1;
                                                             
                           }
              if (val == 2) { //예약이면 실내 2회
                             digitalWrite(2, LOW); // 실내
                              delay(250);
                               digitalWrite(2, HIGH);  
                                delay(250);    
                                digitalWrite(2, LOW); // 실내
                              delay(250);
                               digitalWrite(2, HIGH);  
                               val=1;  //실내    
                               Prestatus = 1; //결과 : 실내
                             }
              if (val == 3) { //외출이면 실내한번 더 누름
                                                         
                                              digitalWrite(2, LOW); 
                                              delay(250);
                                              digitalWrite(2, HIGH);   
                                              val=1;  //실내  
                                              Prestatus = 1;
                                                            
                             }
                                 
                             
               if (val == 4) { //전 상태가 목욕일 땐 직전 상태가 실내인지 외출인지 확인하고 가야함
                              if (Prestatus == 3){ // 전상태가 외출+목욕 이었으면 실내로 바꾸기 위해서 실내 버튼 2번 후 
                                                  digitalWrite(2, LOW); // 실내
                                                  delay(250);
                                                  digitalWrite(2, HIGH);
                                                  delay(250);  
                                                  digitalWrite(2, LOW); // 실내
                                                  delay(250);
                                                  digitalWrite(2, HIGH);
                                                                                                 
                                                  val=1;  //외출
                                                  Prestatus = 1; 
                                                  Sval = 1;
                                                 }
                               if (Prestatus == 1){
                                                  if(Sval == 0){
                                                                digitalWrite(2, LOW); // 실내
                                                                delay(250);
                                                                digitalWrite(2, HIGH);    
                                                                val=1;  //외출
                                                                Prestatus = 1;
                                                               }
                                                   
                                                   }
                              

                              
                                  
                             }
              if(val == 1){
                          server.send(200, "text/plain", "1");
                            else {
                                 server.send(200, "text/plain", "0");
                                 }
                          }
              Sval = 0;
              readString = "";
              
             }
          else {
            if (readString.indexOf("?reserv") > 0)
            {
              digitalWrite(5, LOW); // 목욕
              delay(250);
              digitalWrite(5, HIGH);
              delay(250);
              digitalWrite(4, LOW); // 전원
              delay(250);
              digitalWrite(4, HIGH);
              delay(250);
              digitalWrite(4, LOW); // 전원
              delay(250);
              digitalWrite(4, HIGH);
              delay(250);
              digitalWrite(2, LOW); // 예약
              delay(250);
              digitalWrite(2, HIGH); 
              val=2; //예약
              readString = "";
              
              Sval = 0; //세미밸류 초기화
            }
          
        else {
           if (readString.indexOf("?out") > 0)
          {
              if (val == 1) { //현재상태 실내면 실내 추가로 2회
                             digitalWrite(2, LOW); // 실내
                              delay(250);
                               digitalWrite(2, HIGH);  
                                delay(250);    
                                digitalWrite(2, LOW); // 실내
                              delay(250);
                               digitalWrite(2, HIGH);  
                                val = 3; //외출  
                                Sval = 1; //세미밸류도 외출로 지정   
                                Prestatus = 3;                              
                             }
              if (val == 2) { //예약이면 실내 1회
                             digitalWrite(2, LOW); // 실내
                              delay(250);
                               digitalWrite(2, HIGH);          
                                val = 3; //외출
                                Prestatus = 3;
                             }
              if (val == 3) { //현재 상태도 외출이면 아무것도 안함
                              
                                            val = 3;
                                            Prestatus = 3;
                                            
                                                                   
                             }
                             
               if (val == 4) { //현재 목욕상태인데 외출로 바꾸고싶으면 일단 현재상태가 실내인지 외출인지 확인해야함.
                                 if(Prestatus == 1){ //실내였을경우 3번눌러서 외출로 변경 - 이거 0216-1에서도 3회로 수정해야함
                                                    //실내 3회
                                                    digitalWrite(2, LOW); // 실내1
                                                    delay(250);
                                                    digitalWrite(2, HIGH);
                                                    delay(250);
                                                    digitalWrite(2, LOW); // 실내2
                                                    delay(250);
                                                    digitalWrite(2, HIGH);
                                                    delay(250);
                                                    digitalWrite(2, LOW); // 실내3
                                                    delay(250);
                                                    digitalWrite(2, HIGH);
                                                    val = 3;
                                                    Prestatus = 3;
                                                    Sval = 1;
                                                    } 
                                if(Prestatus == 3){ //외출이었을경우 실내 1번눌러서 바로 그냥 외출로 변경
                                                     //실내 1회
                                                    if (Sval == 0){
                                                                   digitalWrite(2, LOW); // 실내
                                                                   delay(250);
                                                                   digitalWrite(2, HIGH);
                                                                   val = 3;
                                                                   Prestatus = 3;
                                                                  }
                                                    
                                                    }
                              
                             } 
               
           
            readString = "";

           Sval = 0; //세미밸류 초기화
          }
  
         
        else  {
          if (readString.indexOf("?bath") > 0)
            {
               if (val == 4) { //이미 목욕이면 아무것도 안함
                                val = 4;
                                                                                         
                                                         
                              }
               if (val == 1) { //실내이면 목욕으로 변경 하지만 Prestatus를 1로 남김
                                               digitalWrite(5, LOW); // 목욕
                                               delay(250);
                                               digitalWrite(5, HIGH);
                                               val = 4; // 목욕
                                               Prestatus = 1;
                              }
               if (val == 2) { //예약이면 목욕으로 변경
                                               digitalWrite(5, LOW); // 목욕
                                               delay(250);
                                               digitalWrite(5, HIGH);
                                               val = 4; // 목욕
                              }
               if (val == 3) { //외출상태면 목욕으로 변경 하지만 Prestatus를 3으로 남김
                                               digitalWrite(5, LOW); // 목욕
                                               delay(250);
                                               digitalWrite(5, HIGH);
                                               val = 4; // 목욕
                                               Prestatus = 3;
                              }
               Sval = 0;
               readString = "";
               
            }
         else  {
          if (readString.indexOf("?reset") > 0)
            {
              
              val = 1; // 상태 값 실내로(디버깅용)
              Prestatus = 1;
              Sval = 0; //세미밸류 초기화
              
               readString = "";
               
            } 
            
          } // 넷째 reset else 닫음
         
         } //셋째 bath else 닫음
  
       } //둘째 out else 닫음
       
      } //첫 reserv 닫음
       
      readString = "";
      client.stop();
    }
    delay(5);
  }
    }
 }
}
그래서, 최초 실행 시 기본값을 '실내 모드'로 가정하고
각 버튼을 누를 때마다 현재의 모드를 상태값으로 계속 가지고 가는 방법으로 코드를 짰다.
 이 집은 귀뚜라미 보일러 모델명 CTR-5700 으로 무선 또는 IOT를 지원하지 않는 제품이다.
버튼식으로 되어있으니 필시 뚜껑을 벗겨보면 스위치가 있을 터.. 2개 단자를 잠깐 접촉시켜주는 것이 스위치의 역할일테니 웹서버를 가동하고 웹서버에 명령을 주면 릴레이가 각 스위치에 있는 2개의 단자를 잠깐 잠깐씩 접촉 시켜주는 역할을 하도록 하면 되겠다.

아두이노 메가 + 적층형 이더넷 쉴드 + 4개짜리 릴레이를 결선해서 코딩 할 준비를 마쳤다.

요렇게 생긴 녀석으로 귀뚜라미 보일러 모델 CTR-5700 이다
껍데기를 분리해서 보니 3개의 귀여운 스위치가 보인다.
기판 뒷면 모습. 이쪽 면에 납뗌으로 릴레이들을 결선 할 예정
날이 갈수록 납뗌 기술이 레벨업하고 있다. 생각보다 쉽게 납뗌을 했다. 사용한 선은 납뗌이 용이하도록 선 노출부에 미리 납이 발라져 있는 제품으로 알리에서 구매했다. 

생각보다 선이 짧아서..ㅋㅋ 선과 선을 추가로 납뗌을 했다.. 아.. 허접하다. 작업 후 전기테잎으로 깔끔하게 마무리 해주었다.
이런식으로 남는 박스에 넣어서 보일러 컨트롤러가 비스듬하게 시야 방향으로 보이도록 설치 하려고 한다. (이 포스트에서 저는 임시로 종이박스를 사용했으나 화재의 위험이 있을 수 있으니 따라하지마시고 불연소성 소재를 사용하시길 당부드립니다.)
보일러가 또 선이 짧아 각도가 안나오는 관계로 추가 연장선도 납뗌질을 하고야 말았다. 네 다음 남뗌 남용
릴레이 결선 전 모습
완성된 웹서버 첫 구동 모습 동영상

보일러 끄고 켜기 애플워치로 제어 : 아이폰용 앱(Launcher) + 단축어(shortcut) 기능을 활용 동영상

목욕 끄고 켜기 애플워치로 제어  : 아이폰용 앱(Launcher) + 단축어(shortcut) 기능을 활용 동영상



비로소 따뜻한 겨울이 되었다. 흐뭇

댓글

댓글 쓰기

이 블로그의 인기 게시물

#1 (진행 중)아두이노 뇌파센서 헤드셋 만들기(Arduino EEG brain wave headset for psychological test) 만들어 뇌파 읽기

 15년 겨울쯤엔가 TED에서 흥미로운 동영상을 봤다. 뇌파를 통해 컴퓨터 안의 객체를 조종하는 모습을 시연하는 것이었다. 뇌파로 이런 것들이 가능하다는 것이 놀라웠다. 나는 심리학도가 아닌가. 뇌파가 더 정확한 심리검사를 만들 수 있는 도구가 될 수 있다는 생각이 들었다.  예를들어 검사문항(디지털 검사)이 100개짜리 라면 핵심 문항들(각 10번 단위)을 체크할 때마다 심경의 변화, 뇌파변화를 센서(객관적)도 기록하고 디지털검사(주관적)로도 기록해서 함께 데이터화 한다면 더 정확한 심경을 읽어 낼 수 있지 않을까? 라는 생각이었다. 2011년 대학원 다닐 때 컴공과 학부생들 겨울방학 특강으로 Objective-C를 무려1개월간 청강했고, C언어를 무려 2개월동안 학원에 다니면서 공부한 사람이기에 ! -_-;;;; 할 수 있을 것이다............  우선 뇌파센서를 구매해야겠지.  알리 익스프레스에서 구매한 EEG 뇌파센서 kit.  2개를 구매했다.비싸군 ㅠㅠ 배송이 한달정도 걸렸다. 학창시절 라디오 만들기인가..실과시간에 도전해본 납땜 이후로는 처음 해보는 납땜이어서 고생좀 했다. 뇌파를 측정해서 hex 코드로 컴퓨터로 읽어들일 수 있는 상태다. 읽어들인 hex값들을 10진수로 변환하고 유의미한 그래프로 그리거나 데이터화 하는 것이 필요 해 보이지만 아직 받은 값을 10진수로 변환하는 방법을 모르겠다. ㅠㅠ 소스코드는 그냥 단순히 hex값으로 읽어오는것이다보니.. 별거 없다;; 나중에 10진수로 변환하여 읽어들이고 자료화 하는 단계가 필요한 것 같은데 차근차근 진행 해 봐야겠다. 준비물 :  1. HM-08 블루투스 모듈 ($5.30) 2. 아두이노 나노 호환품 ($1.89) 3. direct nerosky e eg  brain...

1# (17.03.19 실패)파이썬(Python)을 활용해 사내식당 금일 메뉴 텔레그램 메세지로 전달받기

우리 사내식당 밥은 아주 맛있다. 맛이 없어서 그런건 아니고.. 그냥 미리 어떤 음식이 나오는지 알고싶을 뿐이다. 맛없는게 나오면 안먹고 라면을 먹기 위해서 만드는 것은 아니다. Brian Park 님의 블로그에서 초등학생 아들 알림장/급식메뉴를 텔레그램으로 알려주는 라즈베리파이 서버 관련 글을 읽고 처음으로 파이썬(웹프로그래밍 언어)에 도전하게 되었다. 의외로 스크립트언어? 사실 난 전문용어 잘 몰라유 ㅠㅠ 틀린게 있으면 바로잡아주세요 ㅠㅠ 처럼 순서대로 실행하고, C언어처럼 중괄호 개념이 아닌 들여쓰기로 구분..하는 것이 나에게는 심플하고 쉽게 느껴졌다. 물론 이런저런 명령어 외워야하는건 어쩔 수 없..ㅠ_ㅠ  아무튼 나의 특기인 따라하기 신공을 통해 맥에 python 2.7을 설치하고, 적절한 편집기로 eclipse를 골랐다. 나처럼 일자무식자가 단순 연습을 하기 위해서는 eclipse보다 그냥 python 기본 제공 앱(?)인 IDLE을 활용하여 코드 한 줄 한 줄이 어떻게 실행되고 왜 실행이 안되는지 확인 할 수 있어서 더 좋은 것 같다. 어쨋든, 삼성웰스토리에 신규 가입을 해서 아이디와 비밀번호를 알아 둔 뒤.. urllib, urllib2, cookielib 라는 라이브러리를 임포트하여 내가 읽어올 페이지 특정 부분에서 내 아이디와 비밀번호 전송값을 대입하여 처리하는.... 말해놓고 나니 무슨말인지...하아  뭐 그런 방식인 것 같다. urlencode가 핵심 키워드가 아닐까 싶다. 그래서~! 지금 아이디와 비번을 입력하여 접속된 화면이 출력되는 것 까지는 확인이 되었다. 이는, 아래에서  f=opener.open('https://www.samsungwelstory.com/member/login.jsp') for line in f: print line.strip() 부분에서처럼 사이트를 열고, 한줄씩 열거하여 보여달라고 요청하여 나온 값들과 사파리에서 소스보기 값들과...

#1 (완료) 아두이노 음주 측정기(Alcohol tester with Arduino)

음주운전을 하지 않는 가장 좋은방법은 대리운전 비용 1만원~1.2만원이 아깝지 않으면서 대리운전 전화번호를 누를 수 있는 정도의 취함 상태인 것 같다. 그래서 생각해본 아이디어가 아예 법적으로 차량에 의무적으로 장치를 설치하도록 하는데 이 장치는 차에 시동을 걸기 전 음주측정을 해야하고 정상 수치내에 있을때만 시동이 걸리는 장치!  물론 조수석에 앉을 누군가가 음주운전을 돕기 위해 대신불어준다면 안되겠지만..ㅠㅠ 아침 출근을 위해 정말 급하게 가글을 하고 나와 출근하려 시동을 걸었는데..가글액에 섞인 알코올 성분때문에 지각을 하는 경우도 생길 수 있겠다만.. 그래도 한번 만들어 보자. 어차피 내게는 차량과 연동할 기술적 지식이 아직 없으므로! 하하하하 06.13 진행 중이나 아직 정리가 안됨 07.01 에 05.28 진행 내용 추가 실제 경찰들 처럼 더더더~ 멘트로 몰입감+정확성(3회 불어서 나온수치의 평균을 활용하는 벙법)을 높일 수 있도록 개선하였다. -_-; Ready 상태. 이후 3,2,1 카운트 후 blow! 그리고 수치를 반복하여 깜빡이며 한다. 이하 소스코드 #include <LiquidCrystal.h> // initialize the library with the numbers of the interface pins LiquidCrystal lcd(12, 11, 5, 4, 3, 2); void setup() {   // set up the LCD's number of columns and rows:   lcd.begin(16, 2); } void loop() { lcd.setCursor(1, 0); //라인1로 커서 위치   lcd.print("Ready...");   delay(4000); lcd.clear();   // set the cursor to column 0, line 1   /...