아마존 에코를 활용한 음성 인식 에어컨 제어

  • |
  • 27 September 2017
Image not Found

에어컨 제어를 위한 회로 설계 및 LIRC 사용

이 포스팅은 하드웨어 대한 지식이 부족한 독자도 이해할 수 있도록 하는 것을 목표로 하여 작성되었습니다.

국민학교 시절에 문방구에서 팔던 사이렌, 라디오 조립 키트를 기억하시나요? 어렸을적 과학시간에 배운 꼬마전구 회로를 만드는 것에 흥미를 느껴서 사이렌 조립키트를 산 적이 있었습니다. 하지만 납땜을 하려면 납이 필요하다는 사실을 모른 채 인두만 사서 납땜을 시도하다가 실패한 가슴아픈 추억이 있습니다. 진짜 납땜은 10년이 지나 학부 논리설계수업에서 처음 해보게 되었습니다. 지금은 회사에서 고수준 언어인 파이썬을 활용해 잠금화면 광고 플랫폼을 만들고 있지만 여전히 마음 한켠에는 하드웨어에 대한 로망을 간직하고 있었습니다.

그래서 이번 기회에 이왕이면 회사에 도움이 될 만한 하드웨어 작품을 만들어보고자 고민하다가 음성인식으로 에어컨을 제어하는 시스템을 만드는 아이디어를 떠올리게 되었습니다. 버즈빌 사무실에는 한 층에 3대의 에어컨이 있습니다. 여름에는 무려 이 3대의 에어컨을 일일히 켜주어야 하는 불편함이 있었습니다. 심지어 2대는 삼성 나머지 한대는 LG 에어컨으로 모델이 달라서 리모콘도 두 개나 필요합니다. 한번에 에어컨을 모두 켤 수 있게 하면 사람들이 좋아하지 않을까, 그리고 간지나게 음성명령으로 제어하면 좋겠다는 생각을 했습니다. 요즘엔 세상이 참 좋아져서 라즈베리파이, 아마존 에코 등을 활용하면 비교적 싼 가격에 원하는 것을 만들 수 있습니다. 프로젝트의 목표는 다음과 같이 정했습니다.

  • “Alexa, turn on the AC”
  • “Alexa, could you please turn off the AC?”

처럼 말하면 알아서 에어컨을 끄고 켜주는 것입니다. 회사에서 슬랙을 메신저로 쓰고 있는데 커스텀 커맨드 기능을 지원하니 아래처럼 슬랙에서도 에어컨을 제어하는 것도 목표로 삼았습니다.

  • /acon - 에어컨 끄기
  • /acoff - 에어컨 켜기
  • /acwarm - 에어컨 약하게
  • /acmedium - 에어컨 중간
  • /accool - 에어컨 세게

에어컨을 직접 제어하는 것은 언뜻 어려울 것 같기도 하지만 의외로 쉬운 방법이 있습니다. 에어컨 리모컨의 동작을 그대로 흉내내는 것입니다. 리모컨은 전자기기와 통신할 때 적외선을 이용합니다. 리모컨의 앞쪽을 보면 조그만한 LED머리가 튀어나와 있는 것을 볼 수 있는데 여기서 적외선이 발생합니다. 이 적외선 LED의 깜빡이는 패턴을 가지고 원하는 신호를 전달하게 됩니다. 아마 가시광선 LED로도 리모컨을 만들 수 있었겠지만 리모컨 버튼 누를때마다 빛이 깜빡인다면 좋아하는 사람은 아무도 없을 것 같습니다. 적외선 LED를 이용하면 에어컨 제어가 가능하다는 것을 알았으니 아래와 같이 전체적인 구조도를 그릴 수 있습니다.

가장 먼저해야 할 것은 회로도를 만드는 것입니다. 리모컨의 동작을 흉내내려면 버튼을 누를 때 어떤 신호가 나오는지 분석이 필요합니다. 이를 위해서 적외선 수신기가 필요합니다. 따라서 만들어야하는 회로는 적외선 발신기와 적외선 수신기 두 부분으로 나누어져 있습니다. 회로는 아래와 같습니다. 적외선 수신기로는 TSOP38238을 선택했습니다. 전원 공급을 위한 3V(+)핀과 GND(-)핀을 연결하고 수신 결과를 받기 위한 핀을 라즈베리파이의 GPIO와 연결합니다. GPIO는 General Purpose Input/Output의 약자로 전압값의 유무를 이용해 출력을 내보내거나 반대로 외부의 값을 읽어들이는 용도로 사용하는 핀입니다. 각각의 핀은 Input 또는 Output 기능중에 하나만 선택하여 사용할 수 있습니다. GPIO는 디지털 회로이기 때문에 0 또는 1만 인식할 수 있습니다. 라즈베리파이의 GPIO 전압은 3.3V 입니다. 따라서 전압이 0V이면 0이고 3.3V이면 1을 의미합니다. GPIO로 사용 가능한 핀이 어떤 것인지는 pinout.xyz 에서 확인 가능합니다. 어떤 GPIO핀을 사용할지는 아무거나 마음대로 선택해도 됩니다. 이왕이면 그라운드(0V), 전원(3.3V, 5V)와 가까운 핀을 선택하는 것이 케이블 연결하기가 편합니다. 또 다른 팁은 다른 특수 기능이 함께 포함되어 있는 GPIO핀은 가능하면 사용하지 않는 것입니다. 주로 시리얼 통신을 지원하기 위한 기능이 많이 있는데 이런것들은 일반적인 기능이 아니기 때문에 모든 핀에 구현되어 있지 않고 특정한 몇몇 핀에만 구현되어 있습니다. 실제로 나중에 빛 감지 센서를 추가로 달려고 보니 이미 사용하던 핀과 충돌이 발생해서 옮기는 작업을 따로 하는 불상사가 발생하였습니다.

적외선 발신기로는 IR333C를 선택했습니다. 발신기 회로는 스위치가 달린 전구 회로와 비슷합니다. 차이점이 있다면 LED에 과전류가 공급되지 않도록 전류를 제한하기 위해 직렬로 68옴 저항을 달았고 물리적인 스위치 대신에 트랜지스터를 달았다는 것입니다. 트랜지스터를 이용하면 전자적으로 전류를 통과시키거나 차단시키는 것이 가능합니다. 라즈베리파이의 GPIO핀과 트랜지스터의 베이스 핀을 연결시켜 LED On/Off 제어가 가능하도록 했습니다. 어떤분은 GPIO에서 3.3V가 나오니 이 출력을 그대로 LED와 연결 시키면 트랜지스터가 없어도 LED 제어가 가능하지 않냐는 의문을 가질 수도 있을 것 같습니다. 하지만 GPIO는 단순히 신호를 전달하기위한 용도로 만들어져있어 LED가 필요로 하는 전력을 공급하기에는 충분하지 않게 설계되어 있습니다. 따라서 전력 공급을 위해 만들어놓은 5V, 3.3V 등의 핀을 이용해 LED에 전력공급을 하고 On/Off 제어는 트랜지스터를 이용해서 구현합니다.

회로에 아직 설명하지 않은 2.2k옴 저항(베이스 저항), 10k옴 저항(풀 다운 저항)이 있는데 이게 왜 필요하고 어느 정도의 저항값을 써야하는지에 대해서는 저의 능력이 부족하여 자세히 설명하기 쉽지 않은 것 같습니다. 덕분에 이 글을 쓰면서 다시 한 번 공부하는 시간을 가졌습니다. 회로를 보호하고 안정적으로 동작하게 하기 위해서 사용한다고 이해하면 좋을 것 같습니다. “LED”, “BJT”, “Mosfet”, “Base resistor”, “Gate resistor”, “Pull-down resistor” 등의 키워드로 검색해보면 자료들을 찾을 수 있습니다. 트랜지스터가 mosfet이냐 BJT냐에 따라서 해당 저항이 필요한 이유가 조금씩 다르므로 구분해서 자료를 찾아보는 것이 좋습니다.

이 회로를 바탕으로 아래와 같이 빵판을 활용해 프로토타입 회로를 완성하였습니다. 빵판도 따로 구매를 해야하는데 인터넷에 “라즈베리파이 입출력 키트"로 찾아보시면 약 만 오천원 가량에 빵판과 커넥터, 점퍼케이블, 종류별 저항등을 한번에 구입할 수 있습니다. 이것저것 알아보기 귀찮으신 분들에게 딱 맞는 제품인 것 같습니다. 적외선 LED는 종류마다 발신 각도에 차이가 있습니다. 발신각도가 좁을수록 LED가 정확하게 에어컨을 향하고 있어야만 신호가 제대로 전달이 됩니다. IR333C는 발신 각도가 40도 입니다. 에어컨이 천장에 달려 있기 때문에 회로가 완성이 되면 에어컨 아래에 위치한 책상에 둘 예정입니다. 하지만 완전히 고정되어 있지 않기 때문에 다른 사람들이 건드려서 위치가 옮겨지게 되면 동작을 하지 않을수도 있습니다. 조금 더 안정적인 동작을 하기 위해서는 발신각도가 더 넓은 LED를 구입하면 됩니다. 전자 부품을 구매하는데 매우 유용한 digikey라는 사이트에서 검색을 해봤습니다. 필터조건을 다음과 같이 설정합니다.

Type = IR
Wavelength = 940nm
Viewing Angle >= 80도
Mounting Type = Through Hole

이렇게 하니 발신각도 140도 짜리 SLED-56-16639 라는 제품을 찾을 수 있었습니다. 국내 사이트에 찾아보니 팔고 있습니다. 하지만 가격이 IR333C보다 많이 비싸서 시험삼아 하나만 구입해봤습니다. 회로를 완성했으니 이제 라즈베리파이에서 이 회로를 제어할 수 있도록 설정하는 것이 필요합니다. 고맙게도 리눅스에서는 LIRC(Linux Infrared Remote Control)라는 패키지가 존재해서 쉽게 적외선 제어와 관련된 프로그램을 만들 수 있도록 도와줍니다.   lirc는 아래 명령을 이용하면 설치가 가능합니다.

sudo apt-get install lirc

mode2, irsend, irrecord등 적외선 제어에 필요한 커맨드들이 설치되고 lircd라는 데몬도 함께 설치됩니다. lircd는 유닉스 도메인 소켓통신을 이용해 적외선 신호를 주고받을 수 있게 해주는 데몬입니다. 프로그램에서는 소켓통신만 하면 뒷단은 알아서 처리해주는 고마운 프로그램입니다. 설치가 완료되고 나면 핀 설정을 해야합니다. 핀을 설정하기 위해 수정해야하는 파일은 다음과 같습니다.

/boot/config.txt

dtoverlay=lirc-rpi,gpio\_in\_pin=23,gpio\_out\_pin=21

/etc/modules

lirc_dev
lirc_rpi gpio_in_pin=23 gpio_out_pin=21

/etc/lirc/hardware.conf

LIRCD_ARGS="--uinput"
DRIVER="default"
DEVICE="/dev/lirc0"
MODULES="lirc_rpi"

파일을 수정하고 나면 라즈베리파이를 재부팅해줍니다. 적외선 신호 캡쳐를 위해 아래와 같이 irrecord 커맨드를 사용했습니다.

irrecord -d /dev/lirc0 lircd.conf

이 커맨드를 실행하면 캡쳐하고 싶은 리모컨의 버튼을 누르면 적외선 수신기를 통해서 캡쳐한 결과를 lircd.conf 파일에 기록해줍니다. 캡쳐 과정은 크게 두 단계로 진행됩니다. 첫번째는 프로토콜을 파악하는 부분입니다. 앞서 정보를 적외선 깜빡임의 패턴을 통해서 전달한다고 했는데 같은 정보를 표현하기 위한 패턴도 여러 종류가 있을 수 있습니다. 이러한 프로토콜을 파악하기 위해 irrecord커맨드에서는 반복적으로 여러 종류의 버튼을 눌러달라고 합니다. 버튼을 수십번 누르다 보면 마침내 프로토콜 파악이 완료되고 실제로 원하는 버튼의 신호를 캡쳐하는 두 번째 단계가 나타납니다. 여기서 버튼의 이름을 입력하고 리모컨의 버튼을 누르면 lircd.conf파일에 프로토콜 명세와 각 버튼의 패킷값이 기록됩니다. lircd.conf 파일의 포맷은 아래와 같습니다.

begin remote

  name  lg-ac
  bits           20
  flags SPACE_ENC|CONST_LENGTH
  eps            30
  aeps          100

  header       3204  9803
  one           575  1497
  zero          575   462
  ptrail        575
  pre_data_bits   8
  pre_data       0x88
  gap          106773
  toggle_bit_mask 0x0

      begin codes
          BTN_0                    0xC0051 0xFF312 # 끄기

          BTN_1                    0x00314 0xFF345 # 냉방/18도/1바람
          BTN_2                    0x00303 0xFF345 # 냉방/18도/2바람
          BTN_3                    0x00347 0xFF345 # 냉방/18도/4바람
      end codes

end remote

윗 부분에는 flags, header, gap등 프로토콜에 대한 복잡한 설정값들이 명세되어 있고 뒷부분에 BTN_0, BTN_1 로 정의되어 있는 부분이 실제로 캡쳐한 버튼에 대한 패킷값입니다. 적외선 프로토콜을 공부하는것이 이번 프로젝트의 목적은 아니기 때문에 이 부분은 irrecord가 만들어준대로 그대로 쓰고 각 버튼의 패킷값을 분석하는데 집중하도록 하겠습니다.

리모컨과 관련해서 기억해야할 아주 중요한 특징이 하나 있습니다. 바로 단방향 통신이라는 것입니다. 리모컨은 에어컨으로 신호를 전달할 수는 있지만 반대로 에어컨의 정보를 받아올 수는 없습니다. 리모컨의 전원 On 버튼을 눌러보면 LCD에 현재 설정된 온도가 표시되어 있습니다. 실제로 여기에 표시되어 있는 값은 에어컨에서 받아온 값이 아닌 리모컨 자체가 보관하고 있던 값이 표시됩니다. 그렇다면 리모컨의 상태와 에어컨의 상태가 일치하지 않는 경우가 발생할 수 있다고 예상할 수 있습니다. 예를 들어, 에어컨 신호가 닿지 않는 곳에서 온도 올리는 버튼을 세번 누르고 다시 에어컨 신호가 닿는 곳에서 온도 올리는 버튼을 한번 누른다면 에어컨과 리모컨의 온도값이 3도가 차이날 수 있습니다.

이러한 문제를 해결하기 위해 리모컨의 프로토콜은 온도를 올려라는 “행위” 보다는 지금 세팅되어야할 온도값의 “상태"를 전송하는 형태로 설계되어 있습니다. 심지어 이 온도의 상태값은 온도 조절 버튼을 누를때만 가는게 아니라 전원 On 버튼을 눌렀을때도 전송이 됩니다. 전원을 켰는데 리모컨과 에어컨의 상태값이 다르면 안되기 때문입니다. 따라서 에어컨 리모컨의 프로토콜의 패킷 구조는 “누른 버튼의 코드 값” + “에어컨의 모든 상태값(온도, 바람 세기, 바람 방향 등)” 과 같이 되어 있다고 예상해볼 수 있습니다. LG 에어컨을 기준으로 캡쳐한 결과는 아래와 같습니다.

begin codes
    BTN_0                    0xC0051 0xFF312 # 끄기

    BTN_1                    0x00314 0xFF345 # 냉방/18도/1바람
    BTN_2                    0x00303 0xFF345 # 냉방/18도/2바람
    BTN_3                    0x00347 0xFF345 # 냉방/18도/4바람

    BTN_4                    0x00617 0xFF345 # 냉방/21도/1바람
    BTN_5                    0x00606 0xFF345 # 냉방/21도/2바람
    BTN_6                    0x0064A 0xFF345 # 냉방/21도/4바람

    BTN_7                    0x0091A 0xFF345 # 냉방/24도/1바람
    BTN_8                    0x00909 0xFF345 # 냉방/24도/2바람
    BTN_9                    0x0094D 0xFF345 # 냉방/24도/4바람

    BTN_10                   0x00C1D 0xFF345 # 냉방/27도/1바람
    BTN_11                   0x00C0C 0xFF345 # 냉방/27도/2바람
    BTN_12                   0x00C50 0xFF345 # 냉방/27도/4바람

    BTN_50                   0x04B10 0xFF345 # 난방/26도/1바람
    BTN_51                   0x04B0F 0xFF345 # 난방/26도/2바람
    BTN_52                   0x04B43 0xFF345 # 난방/26도/4바람

    BTN_53                   0x04D12 0xFF345 # 난방/28도/1바람
    BTN_54                   0x04D01 0xFF345 # 난방/28도/2바람
    BTN_55                   0x04D45 0xFF345 # 난방/28도/4바람

    BTN_56                   0x04F14 0xFF345 # 난방/30도/1바람
    BTN_57                   0x04F03 0xFF345 # 난방/30도/2바람
    BTN_58                   0x04F47 0xFF345 # 난방/30도/4바람
end codes

버튼은 끄기 버튼과 켜기 버튼 딱 두가지만 사용합니다. 켜기 버튼에서도 항상 온도 상태값이 전달된다는 것을 활용하면 온도 조절이 가능합니다. 패킷은 10자리로 구성되어 있는데 왼쪽 5자리는 에어컨의 상태값, 오른쪽 5자리는 누른 버튼의 코드값이라는 것을 알 수 있습니다. 끄기 버튼은 0xFF312, 켜기 버튼은 0xFF345 입니다. 그리고 왼쪽의 상태값에서 0x00303을 더하면 항상 3도씩 올라가는 것을 알 수 있습니다. 이렇게 몇 번 캡쳐를 하고 패턴을 보면 나머지 버튼들은 실제로 캡쳐를 해보지 않아도 어떤 값인지 알 수 있습니다. LG 리모컨의 캡쳐작업은 이렇게 쉽게 끝났습니다. 하지만, 삼성 리모컨은 어째서인지 irrecord를 이용해서 캡쳐시도를 해도 계속해서 오류가 나면서 캡쳐가 되지 않았습니다. 아무래도 표준적이지 않은 형태의 프로토콜을 사용하는 것이 아닌가 의심이 갔고 체크섬 로직이 있다는 이야기도 있어서 분석이 쉽지 않아보였습니다. 하지만 프로토콜이 아무리 복잡하다고 해도 결국에는 LED의 깜빡임으로 모두 표현이 되는 것이기 때문에 raw waveform을 캡쳐해서 그대로 내보낼 방법만 있으면 됩니다. 다행히 mode2라는 유틸리티가 있어 이를 이용하면 수신한 raw waveform을 lircd.conf 포맷으로 출력시킬 수 있습니다. 명령은 아래와 같습니다.

sudo mode2 -m -d /dev/lirc0 > lirc.conf

이렇게 해서 완성한 삼성 에어컨용 lircd.conf 파일은 아래와 같습니다. 파일이 너무 크기때문에 앞쪽에 일부분만 첨부했습니다.

begin remote

  name  samsung-ac
  flags RAW_CODES
  eps            30
  aeps          100

  gap          8929

      begin raw_codes

          name BTN_0
              632    17684     3048     8887      541      444
              552     1440      545      441      556      436
              547      444      550      441      552      439
              554      439      556      435      550     1438
              551      440      554      436      557     1455
              535     1452      545      443      551     1435
              552     1429      548     1437      551     1432
              556     1427      550      441      553      439
              555      437      558      434      549      443
              551      467      527      465      529      463
              542      450      544      448      553      440
              547      444      550      442      553      439
              554      438      556      436      548      444
              549      443      552      440      554      438
              556      436      549      443      550      442
              552      440      554      464      530      462
...

LG 에어컨과는 다르게 버튼 하나 정의하는데 많은 정보가 필요한 것을 알 수 있습니다. 완성한 두개의 파일 내용을 /etc/lirc/lircd.conf 에 넣어두면 lircd 데몬이 시작할 때 읽어들여 명령어를 등록해놓게 됩니다. 각각의 에어컨은 lg-ac, samsung-ac 로 이름을 지정하였습니다. 파일 하나에 두 디바이스의 명령을 한번에 관리하려니 불편하여 파일을 두개로 쪼개고 싶었습니다. 그래서 lircd.conf 파일의 내용을 아래와 같이 변경하였습니다.

include "conf.d/samsung-ac.conf"
include "conf.d/lg-ac.conf"

그리고 conf.d 디렉토리를 만들고 이 안에 각각 모델에 대한 정의가 들어있는 파일을 넣어두니 원하는대로 동작하였습니다. 사실 include “conf.d/*” 처럼 glob pattern을 적용하고 싶었지만 어째서인지 동작하지 않아서 어쩔 수 없이 각각의 파일 이름을 지정하였습니다. 이제 irsend 명령을 이용하면 적외선 발신기를 제어할 수 있습니다. 예제 명령은 아래와 같습니다.

irsend SEND_ONCE lg-ac BTN_1
irsend SEND_ONCE samsung-ac BTN_2

이렇게해서 라즈베리파이의 쉘에서 irsend 커맨드를 이용해 에어컨을 마음대로 제어할 수 있게 되었습니다. 궁금하신 분들을 위해서 재료비를 공개합니다.

  • 라즈베리파이3 + 공식케이스 + 방열판 = 53,350원
  • SD카드 = 4,410원
  • 트랜지스터 = 700원
  • 적외선 LED = 400원
  • 저항 = 100원
  • 만능기판 = 1,300원
  • 만능기판 다리 = 100원
  • 점퍼 케이블 = 300원
  • 총 = 60,660원

올해 초에 RASPBERRY PI ZERO W라는 제품이 나왔습니다. 라즈베리파이3에서 몇가지 기능을 빼서 싸게 만든 제품인데 해외에서 10불에 판매하고 있습니다. 한국에서는 이것저것 끼워서 비싸게 팔고 있네요. 이번 프로젝트에서 필요로 하는 기능은 다 갖추고 있기에 기회가 되면 구매해서 테스트해볼 예정입니다. 만능기판의 경우 PCB 재질이 여러 종류가 있는데 가장 저렴한 재질인 종이 페놀은 미관상 좋지 못해서 조금 더 예쁘고 튼튼한 에폭시 재질로 구매했습니다. 누런 색 기판이 페놀 재질이고 초록색 기판이 에폭시 재질입니다. 저항은 허용 전력에 따라서 크기가 다르게 나옵니다. 1/2W는 크기가 너무 크고 1/4W가 일반적으로 쓰기에 무난합니다. 더 작은 1/8W을 써도 됩니다. LED가 잠시 깜빡일때만 전류가 흐르므로 허용 전력은 충분합니다. 아래는 납땜을 위한 준비물 비용입니다.

  • 라즈베리파이 입출력 키트 = 14,600
  • 테프론 와이어 AWG30 = 5,380원
  • Kester 유연납 1.0mm /50g = 4,400원
  • 멀티미터 FLUKE-101 = 43,500원
  • HAKKO FX-888D 디지털인두기 = 143,550원
  • 교체용인두팁 T18-K = 11,880원
  • 니퍼/핀셋 = 집에 있는 것
  • 총 = 223,310원

납땜 준비물 구매하는데 많은 돈을 썼는데 저처럼 충동구매로 비싼 인두와 멀티미터를 사지만 않으면 싼 가격으로 장비들을 마련할 수 있습니다. 혹시나 회로가 동작하지 않을 때 문제를 빠르게 파악하기 위해서 멀티미터를 꼭 구입하는 것을 추천합니다. 고급 제품인 FLUKE사 모델이 비싸다면 VC99라는 모델이 가성비가 좋다고 합니다. 지금까지 에어컨 제어를 위한 회로 설계 및 LIRC 사용에 대해 알아봤습니다. 이후의 서버 네트워크 구축 및 아마존 에코/슬랙 연동과 배포 및 테스트 자동화에 대한 프로젝트의 자세한 정보는 GitHub repository에 공개되어 있습니다. 긴 글 읽어주셔 감사합니다.

References

You May Also Like

버즈빌, 아마도 당신이 원하던 회사!

지원하기