텔레그램으로 제어하는 홈 오토메이션 프로젝트 – 홈파이

  1. 홈파이: 텔레그램 메신저로 제어하는 홈 오토메이션 프로젝트
  2. 홈파이 : 라즈베리 파이에 설치하는 방법
  3. 홈파이 : 예제 센서 장치 만드는 방법
  4. 홈파이 : 동작 시나리오와 프로토콜
  5. 홈파이 : 명령어 리스트
  6. 홈파이 : 일회용 컵을 재활용한 스마트 화분 연동하기

.

여기서는 홈파이와 연동되는 센서 장치를 만들수 있도록 예제 장치 만드는 방법을 소개합니다. 여기 사용된 아두이노 코드를 참고하시면 빠르고 편리하게 센서 장치를 만들어 무선으로 연동할 수 있으며, 텔레그램 메신저로 제어도 할 수 있습니다.

homechat_arduino_2

.


홈 오토메이션 장치 만들기

홈파이 서버가 정확히 동작하기 위해서는 아두이노 등으로 장치를 만들 때 홈파이에서 정의해 둔 프로토콜에 맞게 동작하도록 만들어야 합니다.

예를 들어  온습도 센서와 함께 가습기 전원을 제어하는 아두이노 장치를 만들었다고 가정해 보겠습니다. 그럼 아래와 같은 과정으로 동작해야 합니다.

  1. 온습도 센서는 최초 부팅시 초기화 과정에서 등록(Registration) 신호를 무선으로 보내줍니다. 그리고 이후도로 원하는 간격으로 등록 신호를 보내 존재를 알립니다.
  2. 초기화가 끝나면 이후부터는 온습도를 측정하는 작업을 반복합니다. 그리고 적당한 간격으로 온습도 센서값을 보내줍니다. (Update)
  3. 그리고 HC-11 RF 모듈을 통해 들어오는 패킷이 있는지도 계속 체크해야 합니다. 그래서 자신에게로 오는 커맨드가 감지 되었을 때 그에 맞는 동작을 해야 합니다. 커맨드는 여러가지가 미리 정의되어 있는데 최소한 핑 체크(Ping), 등록(Registration request), 업데이트(Sensor update), 제어신호(Control signal) 에는 반응하도록 만들기를 권장합니다.
  4. Control signal은 홈파이에서 사용자가 내린 제어신호입니다. 신호값에 맞게 가습기 전원이 on/off 되도록 해야할 것입니다. 홈파이에서 이 장치에서 수행가능한 제어신호가 무엇인지 인식하기 위해서는 등록(Registration) 과정을 수행할 때 미리 지정된 코드를 넣어줘야 합니다.

이와 같은 시나리오대로 동작하는 예제 장치를 만드는 방법과 예제 코드를 소개하겠습니다. 장치 제작 및 테스트를 위해서는 아래와 같은 부품들이 필요합니다.

준비물 :

  • 아두이노 보드
  • DHT11(or DHT22) 센서
  • LED 및 저항 1개씩
  • HC-11 433MHz RF 모듈

14001416410

.


연결 방법

이런 작업들을 모두 수행하도록 아두이노 코드를 짜는 것은 어려울 수도 있으므로 미리 예제코드를 마련해 뒀습니다. 이 예제는 DHT22(또는 DHT11) 온습도 센서를 이용해서 온습도를 측정해서 전송하고, 홈파이에서 제어신호가 오면 연결된 LED 를 on/off 시키는 예제입니다. 부품을 아래와 같이 연결합니다.

  • DHT11 신호 핀 –> 아두이노 디지털 6번 핀
  • DHT11 VCC –> 아두이노 5V
  • DHT11 GND –> 아두이노 GND
  • 아두이노 디지털 8번 핀 –> 460옴 저항 –> LED –> 아두이노 GND
  • HC-11 TX –> 아두이노 2번 핀
  • HC-11 RX –> 아두이노 3번 핀
  • HC-11 VCC –> 아두이노 5V
  • HC-11 GND –> 아두이노 GND

DHT22 센서를 통해 값을 읽기 위해 DHT 라이브러리가 필요합니다. [링크]의 게시글을 확인하시면 센서의 사용법과 라이브러리를 다운로드 받을 수 있습니다. 라이브러리를 다운로드 받아 설치까지 해주세요.

HC-11 모듈은 아두이노의 D2, D3에 연결하고 SoftwareSerial 라이브러리를 통해 제어합니다. 근래의 아두이노 개발환경에는 이미 설치되어 있는 라이브러리입니다.

.


예제 소스 업로드 및 분석

예제 소스는 앞서 설명한 시나리오대로 동작합니다. 아래 링크에서 example 폴더의 아두이노 예제를 다운로드 받으세요.

https://github.com/godstale/HomePy

소스를 받으면 가장 먼저 자신의 장치에 맞는 category1, category2, ID 코드를 지정해줘야 합니다. 예제코드 상단에서 아래 코드를 찾으면 됩니다.

const char SEND_START1 = 0x55;    // start byte 1
const char SEND_START2 = 0x01;    // start byte 2
const char SEND_CAT1 = 0x01;      // category 1
const char SEND_CAT2 = 0x01;      // category 2
const char SEND_LOC = 0x01;       // location number
const char SEND_END1 = 0xFF;      // end byte 1

category1, category2 코드를 통해 센서 장치의 종류를 표현합니다. 기본값을 그대로 사용하면 온습도센서(0x01, 0x01)로 인식합니다. 원하신다면 이 값을 바꿔도 좋습니다. 사용 가능한 코드 리스트나 보다 상세한 내용은 홈파이 프로토콜을 참고하세요.

https://www.hardcopyworld.com/?p=2236

여기서 SEND_LOC = 0x01 부분은 실제로는 ID 역할을 하는 값입니다. 즉, 같은 장치를 여러개 만든다면 ID 값이 모두 달라야 합니다. 그리고 ID 값은 특정 값의 범위에 있을 때 장치가 설치된 위치를 표시하도록 고려되어 있습니다. 이 부분 역시 홈파이 프로토콜에서 확인하실 수 있습니다. 0x01로 지정할 경우는 위치 “미지정” 상태가 되며 다른 장치와 구분하는 ID 역할만 합니다.

이어지는 아래 코드를 통해 센서 장치가 처리할 수 있는 제어 신호를 지정합니다. 제어할 동작의 종류와 입력 가능한 데이터 타입을 설정합니다. 우리는 LED를 on/off 제어가 가능하도록 할 계획이기 때문에 아래와 같이2개의 코드값을 지정해 줬습니다.

// Define control signal 1 type : general light emitting device
// Refer to the HomePy protocol document
#define CONTROL1_TYPE 0x07
// Define control signal 1 data type : boolean (0: off, 1: on)
// Refer to the HomePy protocol document
#define CONTROL1_DATA_TYPE 0x01

이 값은 추후 등록(Registration) 과정에서 홈파이 서버로 전송됩니다. 홈파이 서버는 이 코드를 인식해서 사용자에게 보여줍니다.

만약 4종류의 제어가 가능하도록 하고 싶다면 위와 같은 CONTROL2, 3, 4에 해당하는 코드도 지정해서 등록 과정 때 홈파이 서버에 전송하면 됩니다. 이 부분은 추후 다시 설명합니다.

아래는 아두이노의 초기화 함수인 setup() 함수의 내용입니다.

void setup() {
  // for debug
  Serial.begin(9600);
  Serial.println("HomePy thermometer and LED control test !!");

  // initialization
  dht.begin();
  hc11.begin(9600);
  pinMode(LEDPIN, OUTPUT);
  randomSeed(analogRead(0));  // for test, I'll touch the value slightly
  
  // Send device registration command to server 2 times
  for(int i=0; i<2; i++) {
    sendRegistration();
    prevRegistrationTime = millis();
    delay(3000);
  }
}

센서 장치는 서버와 연동을 위해 자신의 존재를 알리는 등록(Registration) 과정을 거쳐야 합니다. 이 과정을 거치는 부분이 for 루프문 안에서 sendRegistration() 함수를 호출하는 부분입니다. 1회만 호출할 경우 다른 장치와의 충돌로 패킷이 누락될 수 있으므로 충분한 간격을 두고 2회 호출하도록 했습니다. 등록은 여러번 해도 하나의 장치로만 서버에 인식됩니다.

sendRegistration() 함수는 아래와 같이 패킷을 보냅니다.

void sendRegistration() {
  hc11.write(SEND_START1);    // Start byte 1
  hc11.write(SEND_START2);    // Start byte 2
  hc11.write(SEND_CAT1);      // Category 1
  hc11.write(SEND_CAT2);      // Category 2
  hc11.write(SEND_ID);       // ID
  hc11.write(CMD_REGISTER_DEVICE);  // Command
  hc11.write((byte)CONTROL1_TYPE);        // control signal 1 type
  hc11.write((byte)CONTROL1_DATA_TYPE);   // control signal 1 data type
  hc11.write((byte)0x00);    // control signal 2 type - NA
  hc11.write((byte)0x00);    // control signal 2 data type - NA
  hc11.write((byte)0x00);
  hc11.write((byte)0x00);
  hc11.write((byte)0x00);    // control signal 4 type - NA
  hc11.write((byte)0x00);    // control signal 4 data type - NA
  hc11.write(SEND_END1);
}

총 15byte를 프로토콜 구성에 맞게 순서대로 보내줍니다. 앞서 언급한대로 제어 가능한 신호를 여러개 설정하고 싶다면 control signal 2, 3, 4 에 해당되는 패킷에 원하는 코드를 적어주면 됩니다. 사용가능한 코드는 프로토콜 문서를 참고하세요.

setup() 에서 등록 과정이 완료되면 이제 loop() 함수를 반복하면 홈파이 서버와 연동해서 동작합니다.

void loop() {
  //----- Send registration request
  if(millis() - prevRegistrationTime > REGISTRATION_INTERVAL) {
    sendRegistration();
    prevRegistrationTime = millis();
    delay(100);
  }
  
  //----- Send temperature and humidity info to remote
  if(millis() - prevReadTime > SENDING_INTERVAL) {
    int h = (int)dht.readHumidity() + random(0, 3);
    int t = (int)dht.readTemperature() + random(0, 2);
    
    // Check if any reads failed and exit early (to try again).
    if (isnan(h) || isnan(t)) {
      return;
    }

    // send data to server
    sendSensorValue(h, t, 0, 0);
    prevReadTime = millis();
  }
  
  //----- Parsed received packet
  int command = CMD_NONE;
  
  if (hc11.available()>0)  {
    // Get incoming byte
    char in_byte = 0x00;
    in_byte = hc11.read();
    command = parseCommand(in_byte);
  }  // End of if (hcSerial.available()>0)

  //--------------------------------------------
  // Process commands
  //--------------------------------------------
  if(command > CMD_NONE) {
    processCommand(command);
  }

}  // End of loop()

가장 먼저 등장하는 코드는 주기적으로 홈파이 서버에 등록 과정을 진행하는 코드입니다. 너무 짧지 않은 간격으로, 원하는 시간 간격을 두고 등록 과정을 반복해야 합니다. 홈파이 서버가 재시작하거나 등록 패킷이 누락될 수 있기 때문입니다. sendRegistration() 함수만 호출해주면 됩니다.

  if(millis() - prevRegistrationTime > REGISTRATION_INTERVAL) {
    sendRegistration();
    prevRegistrationTime = millis();
    delay(100);
  }

이후 등장하는 코드는 온습도 센서에서 값을 읽어 홈파이 서버로 전송하는 코드입니다. 홈파이 서버는 수 많은 센서 장치를 상대할 수 있으므로 업데이트 간격을 너무 짧지 않게 설정해야 합니다. 일정 시간 동안은 아두이노 내부에서 값을 읽어 평균값으로 저장해 뒀다 전송하는 것이 좋습니다. 전송된 데이터는 서버에 누적됩니다. sendSensorValue() 함수를 호출하면 온-습도 센서값 2개를 전송합니다.

  //----- Send temperature and humidity info to remote
  if(millis() - prevReadTime > SENDING_INTERVAL) {
    // Reading temperature or humidity takes about 250 milliseconds!
    ......
    // send data to server
    sendSensorValue(h, t, 0, 0);
    prevReadTime = millis();
  }

홈파이 서버에서 보내주는 데이터도 처리해줘야 합니다. 데이터 파싱은 parseCommand() 함수에서 알아서 해줍니다. 수신된 데이터에서 현재 장치에 해당되는 커맨드가 발견 되었을 때 command 값을 리턴해 줍니다. command 에 맞게 적절히 작업을 해주면 됩니다. parseCommand() 함수가 command 를 발견하면 패킷에 같이 실려있던 데이터 값을 value1, value2, value3, value4 전역 변수에 저장해 둡니다. value1 ~4 까지의 값은 제어 신호가 왔을 때 4개의 제어신호 값을 담는 역할을 합니다.

  //----- Parsed received packet
  int command = CMD_NONE;
  
  if (hc11.available()>0)  {
    // Get incoming byte
    char in_byte = 0;
    in_byte = hc11.read();
    command = parseCommand(in_byte);
  }

processCommand() 함수를 통해 홈파이 서버가 보내준 커맨드에 맞는 동작을 수행할 수 있습니다.

  //--------------------------------------------
  // Process commands
  //--------------------------------------------
  if(command > CMD_NONE) {
    processCommand(command);
  }

핑 테스트(CMD_PING), 센서값 업데이트 요청(CMD_REQUEST_SENSOR_VAL) 커맨드는 바로 해당 응답을 보내주면 됩니다. 그 외에 제어 신호(CMD_CONTROL_SIGNAL) 커맨드가 왔을 때는 해당 동작을 수행해야 합니다. 이번 예제로 만든 장치에서는 LED를 on/off 시키는 동작을 해야합니다. value1 값에 1이 들어오면 LED를 on 시키고, 0 일 경우 LED를 off 시킵니다.

상세 내용은 processCommand() 함수를 참고하면 됩니다. 다른 장치를 만들 경우 이 부분도 원하는 형태대로 수정해주시면 됩니다.
.


나만의 센서 장치 만들기

예제에 있는 많은 코드들을 재사용할 수 있습니다. 특히 패킷을 서버로 보내는 함수들은 그대로 사용하고, 코드 상단의 특정 패킷 코드들 정의만 바꿔주면 됩니다.

홈파이 서버에서 온 커맨드를 처리하는 부분도 processCommand() 함수를 수정해서 사용하면 됩니다.

아두이노의 메인 loop() 함수는 온습도 센서를 읽어와서 처리하는 부분만 수정하고 그대로 사용하셔도 됩니다. 가급적 등록(Registration), 센서값 업데이트 시간은 원하는대로 조정해서 사용하시길 권장합니다.

homechat_arduino_1

.