2016년 5월 31일 화요일

Smart Air Conditioner Controller (에어컨 자동으로 조종하기)

친구 녀석과 '양꼬치엔 칭따오~' 를 하면서 수다를 떨다가 '잘 때 에어컨 껐다 켰다 하는 것도 일이다 야~' 라고 하길래 '그럼 내가 자동으로 해줄까?' 라고 했더니 부품값 줄테니 해 달라고 해서 집에 들어오자마자 뚝딱뚝딱 하드웨어만 만들어 봤다. 이젠 납땜에 재미가 붙어서 한 15분 정도만에 완성했다.
스마트 에어컨 리모컨 (앞면) 
스마트 에어컨 리모컨 (뒷면)
구성품은 Arduino Pro mini 3V, Grove 온도 센서, Ky-005 (IR Transmitter), HM-10 (블루투스) 그리고 배터리 케이스이다. 호기심에 사본 양면 기판에 붙였다. 양면기판을 이런 용도로 쓰는게 아닌것 같은데...ㅋㅋㅋ sketch code 를 만들지도 않고 하드웨어를 먼저 구성할만큼 이젠 아두이노에 익숙해 진걸까. 소프트웨어 쟁이한테 하드웨어가 더 쉬워지면 안되느데 ㅠㅜ

소요비용을 계산 해 보면,
1. arduino pro mini 3v : 4,700 원
2. HM-10 Bluetooth : 12,000 원
3. grove temperature sensor : 3,710 원
4. key-005 IR transmitter : 3,500 원
5. 양면 기판 : 1,000 원
6. 배터리 케이스 : 1,500 원
7. 건전지 : 1,000 원
대충 27,000원 정도 나왔다. 우워.. 별로 안들줄 알았는데 HM-10 이 비싸서 좀 많이 나왔다.
알리에서 구매하면 반값이상 줄어 들텐데 국내 쇼핑몰에서 빨리 받아보는 맛으로 그냥.. orz

친구녀석의 요구사항은 이랬다.
기본 : 특정 온도가 되면 자동으로 켜질 것
1. 시작온도와 몇 분동안 동작할지 설정
2. 시작온도와 몇 도까지 동작할지 설정
3. 1, 2 의 시간, 온도 조건이 만족하면 꺼질 것

BLE 통신을 하기 때문에 MSMble 로 text 세팅해도 된다고는 하는데, 친구 녀석도 Gear S2 를 차고 다니니 기어앱으로 만들어 줄까 한다. 우선 앱은 나중에 만들더라도 위 요구 사항을 만족하는 sketch 부터 만들어야 할텐데... 리모컨 신호는 IR Receiver 로 LG 에어컨 신호를 dump 뜨면 되는 일인고, 온도 센서로 현재 온도를 체크 하면 되는데, 문제는 시간이 문제이다.

'현재 시간을 어떻게 가져오지? GPS 도 없고 Wifi 도 없으니 NTP (Network Time Protocol) 도 사용할 수 없지 않은가' 라는 생각을 하다가 BLE 로 시간을 써 주는 아이디어가 떠올랐다. 최초 부팅때는 시간이 설정되지 않는 문제가 있지만, 어차피 설정을 하려면 App 을 켜야 하니까 큰 문제는 없겠지. 음... 뭔가 찜찜하긴 한데 나중에 좀 더 시간을 갖고 생각 해 봐야 겠다.

그럼 만들어 볼까?
아두이노는 이렇게 구성하고, sketch 는 아래와 같이 작성 했다.

#include <SoftwareSerial.h>
#include <Time.h>
#include <IRremote.h>
#define TIME_MSG_LEN  11
SoftwareSerial bb(9, 8);
String recv = "";
time_t apptime = 0;
time_t cctime = 0;
time_t rtime = 0;
int rmin = 0;
float stemp;
float rtemp;
int a;
float ctemp;
int B=3975;
float resistance;
int bset = 0;
IRsend irsend;
int status = 0;
/* turn on */
unsigned int turnon[59] = {...};
/* turn off */
unsigned int turnoff[59] = {...};
/* 22.0 */
unsigned int settemp_22_0[59] = {...};
/* 22.5 */
unsigned int settemp_22_5[59] = {...};
/* 23 */
unsigned int settemp_23_0[59] = {...};
/* 23.5 */
unsigned int settemp_23_5[59] = {...};
/* 24 */
unsigned int settemp_24_0[59] = {...};
float StrToFloat(String str){
  char carray[str.length() + 1];
  str.toCharArray(carray, sizeof(carray));
  return atof(carray);
}
void setup()
{
  Serial.begin(9600);
  bb.begin(9600);
  bb.println("hello aircon ");
}
void loop()
{
  while (bb.available()){
    char data = bb.read();
    recv.concat(data);
    if (data == '&'){
      if(recv[0] == '@'){
        /* time setting */
        Serial.println(recv);
        for(int i=1; i<TIME_MSG_LEN; i++){
          if( recv[i] >= '0' && recv[i] <= '9'){
            apptime = (10 * apptime) + (recv[i] - '0');
          }
        }
        setTime(apptime);
        Serial.print(day());
        Serial.print(" ");
        Serial.print(month());
        Serial.print(" ");
        Serial.println(year());
      } else if(recv[0] == '#'){
        char srecv[recv.length()];
        recv.toCharArray(srecv, sizeof(srecv));
        Serial.println(srecv);
        String temp = "24.0";
        String mins = "30";
        String dtemp = "24.0";
        /* command */
        if(srecv[1] == '$'){
          /* Temp|Min format : #$22.0|30 */
          //sscanf(srecv, "#$%f|%f", &temp, &mins);
          temp[0] = srecv[2];
          temp[1] = srecv[3];
          temp[2] = srecv[4];
          temp[3] = srecv[5];
          stemp = StrToFloat(temp);
          rtemp = stemp;
          rtime = now();
          mins[0] = srecv[7];
          mins[1] = srecv[8];
          rmin = StrToFloat(mins);        
          Serial.print("start temperature = ");
          Serial.println(stemp);
          Serial.print("mins = ");
          Serial.println(rmin);
          bset = 1;
        }else if(srecv[1] == '^'){
          temp[0] = srecv[2];
          temp[1] = srecv[3];
          temp[2] = srecv[4];
          temp[3] = srecv[5];
          stemp = StrToFloat(temp);
          rtime = now();
          dtemp[0] = srecv[7];
          dtemp[1] = srecv[8];
          dtemp[2] = srecv[9];
          dtemp[3] = srecv[10];
          rtemp = StrToFloat(dtemp);        
          Serial.print("start temperature = ");
          Serial.println(stemp);
          Serial.print("deat temperature = ");
          Serial.println(dtemp);
        }
      }
      recv = "";
      apptime = 0;
    }
  }

  if (bset) {
    int khz = 38;
    a = analogRead(0);
    resistance = (float)(1023-a)*10000/a;
    ctemp = 1/(log(resistance/10000)/B+1/298.15)-273.15;

    Serial.print("Current temperature is ");
    Serial.println(ctemp);
    cctime = now();
    Serial.print("remain time ");
    Serial.print((rmin*60)-(cctime-rtime));
    Serial.println(" s");  
    if((ctemp > stemp) && ((cctime-rtime)<rmin*60)) {
      /* turn on & set temperature */
      if (!status) {
        status = 1;      
        Serial.println("turn on and set temp ");
        irsend.sendRaw(turnon, sizeof(turnon) / sizeof(turnon[0]), khz);
        if (rtemp == 22.0)
            irsend.sendRaw(settemp_22_0, sizeof(settemp_22_0) / sizeof(settemp_22_0[0]), khz);
        else if (rtemp == 22.5)
            irsend.sendRaw(settemp_22_5, sizeof(settemp_22_5) / sizeof(settemp_22_5[0]), khz);
        else if (rtemp == 23.0)
            irsend.sendRaw(settemp_23_0, sizeof(settemp_23_0) / sizeof(settemp_23_0[0]), khz);
        else if (rtemp == 23.5)
            irsend.sendRaw(settemp_23_5, sizeof(settemp_23_5) / sizeof(settemp_23_5[0]), khz);
        else if (rtemp == 24.0)
            irsend.sendRaw(settemp_24_0, sizeof(settemp_24_0) / sizeof(settemp_24_0[0]), khz);
      }
    } else {
      /* turn off */
      if (status) {
        status = 0;
        Serial.println("turn off");
        irsend.sendRaw(turnoff, sizeof(turnoff) / sizeof(turnoff[0]), khz);
      }
    }
  }
  delay(1000);
}
동작 방식은 간단하다.
1. BLE 로 연결할 때 시간을 setting 해 준다.
 '$ date +%s' 로 1970-01-01 00:00:00 UTC 부터의 초 값을 얻어 올 수 있다. 이것을 setting 한다.
 web 에서는 http://www.epochconverter.com/ 에서 값을 구할 수 있다.
2. prefix 는 @ 과 #, 2차 prefix 는 $과 ^ 이다.
  - @ 는 시간 setting
  - # 는 command
  - $ 는 # 과 같이 '시작온도|시간' command
  - ^ 는 # 과 같이 '시작온도|목표온도' command

테스트 하려면,
1. MSMble 를 실행하고
2. '@146467881' + send
3. '#$22.0|30' + send
처럼 하면 된다.

우하하.. 친구녀석에게 양꼬치 또 얻어 먹으러 가야쥐~ ㅋㅋㅋ


댓글 1개:

김상민 :

전자과나 소프트웨어쪽 전공자 이신지..? 실력이 어마어마 하시네요..