AWS 비용 최적화 Part 1: 버즈빌은 어떻게 월 1억 이상의 AWS 비용을 절약할 수 있었을까
버즈빌은 2023년 한 해 동안 월간 약 1.2억, 연 기준으로 14억에 달하는 AWS 비용을 절약하였습니다. 그 경험과 팁을 여러 차례에 걸쳐 공유합니다. AWS 비용 최적화 Part 1: 버즈빌은 어떻게 월 1억 이상의 AWS 비용을 절약할 수 있었을까 (준비중) …
Read Article이 포스팅은 하드웨어 대한 지식이 부족한 독자도 이해할 수 있도록 하는 것을 목표로 하여 작성되었습니다.
국민학교 시절에 문방구에서 팔던 사이렌, 라디오 조립 키트를 기억하시나요? 어렸을적 과학시간에 배운 꼬마전구 회로를 만드는 것에 흥미를 느껴서 사이렌 조립키트를 산 적이 있었습니다. 하지만 납땜을 하려면 납이 필요하다는 사실을 모른 채 인두만 사서 납땜을 시도하다가 실패한 가슴아픈 추억이 있습니다. 진짜 납땜은 10년이 지나 학부 논리설계수업에서 처음 해보게 되었습니다. 지금은 회사에서 고수준 언어인 파이썬을 활용해 잠금화면 광고 플랫폼을 만들고 있지만 여전히 마음 한켠에는 하드웨어에 대한 로망을 간직하고 있었습니다.
그래서 이번 기회에 이왕이면 회사에 도움이 될 만한 하드웨어 작품을 만들어보고자 고민하다가 음성인식으로 에어컨을 제어하는 시스템을 만드는 아이디어를 떠올리게 되었습니다. 버즈빌 사무실에는 한 층에 3대의 에어컨이 있습니다. 여름에는 무려 이 3대의 에어컨을 일일히 켜주어야 하는 불편함이 있었습니다. 심지어 2대는 삼성 나머지 한대는 LG 에어컨으로 모델이 달라서 리모콘도 두 개나 필요합니다. 한번에 에어컨을 모두 켤 수 있게 하면 사람들이 좋아하지 않을까, 그리고 간지나게 음성명령으로 제어하면 좋겠다는 생각을 했습니다. 요즘엔 세상이 참 좋아져서 라즈베리파이, 아마존 에코 등을 활용하면 비교적 싼 가격에 원하는 것을 만들 수 있습니다. 프로젝트의 목표는 다음과 같이 정했습니다.
처럼 말하면 알아서 에어컨을 끄고 켜주는 것입니다. 회사에서 슬랙을 메신저로 쓰고 있는데 커스텀 커맨드 기능을 지원하니 아래처럼 슬랙에서도 에어컨을 제어하는 것도 목표로 삼았습니다.
에어컨을 직접 제어하는 것은 언뜻 어려울 것 같기도 하지만 의외로 쉬운 방법이 있습니다. 에어컨 리모컨의 동작을 그대로 흉내내는 것입니다. 리모컨은 전자기기와 통신할 때 적외선을 이용합니다. 리모컨의 앞쪽을 보면 조그만한 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 커맨드를 이용해 에어컨을 마음대로 제어할 수 있게 되었습니다. 궁금하신 분들을 위해서 재료비를 공개합니다.
올해 초에 RASPBERRY PI ZERO W라는 제품이 나왔습니다. 라즈베리파이3에서 몇가지 기능을 빼서 싸게 만든 제품인데 해외에서 10불에 판매하고 있습니다. 한국에서는 이것저것 끼워서 비싸게 팔고 있네요. 이번 프로젝트에서 필요로 하는 기능은 다 갖추고 있기에 기회가 되면 구매해서 테스트해볼 예정입니다. 만능기판의 경우 PCB 재질이 여러 종류가 있는데 가장 저렴한 재질인 종이 페놀은 미관상 좋지 못해서 조금 더 예쁘고 튼튼한 에폭시 재질로 구매했습니다. 누런 색 기판이 페놀 재질이고 초록색 기판이 에폭시 재질입니다. 저항은 허용 전력에 따라서 크기가 다르게 나옵니다. 1/2W는 크기가 너무 크고 1/4W가 일반적으로 쓰기에 무난합니다. 더 작은 1/8W을 써도 됩니다. LED가 잠시 깜빡일때만 전류가 흐르므로 허용 전력은 충분합니다. 아래는 납땜을 위한 준비물 비용입니다.
납땜 준비물 구매하는데 많은 돈을 썼는데 저처럼 충동구매로 비싼 인두와 멀티미터를 사지만 않으면 싼 가격으로 장비들을 마련할 수 있습니다. 혹시나 회로가 동작하지 않을 때 문제를 빠르게 파악하기 위해서 멀티미터를 꼭 구입하는 것을 추천합니다. 고급 제품인 FLUKE사 모델이 비싸다면 VC99라는 모델이 가성비가 좋다고 합니다. 지금까지 에어컨 제어를 위한 회로 설계 및 LIRC 사용에 대해 알아봤습니다. 이후의 서버 네트워크 구축 및 아마존 에코/슬랙 연동과 배포 및 테스트 자동화에 대한 프로젝트의 자세한 정보는 GitHub repository에 공개되어 있습니다. 긴 글 읽어주셔 감사합니다.
버즈빌은 2023년 한 해 동안 월간 약 1.2억, 연 기준으로 14억에 달하는 AWS 비용을 절약하였습니다. 그 경험과 팁을 여러 차례에 걸쳐 공유합니다. AWS 비용 최적화 Part 1: 버즈빌은 어떻게 월 1억 이상의 AWS 비용을 절약할 수 있었을까 (준비중) …
Read Article들어가며 안녕하세요, 버즈빌 데이터 엔지니어 Abel 입니다. 이번 포스팅에서는 데이터 파이프라인 CI 테스트에 소요되는 시간을 어떻게 7분대에서 3분대로 개선하였는지에 대해 소개하려 합니다. 배경 이전에 버즈빌의 데이터 플랫폼 팀에서 ‘셀프 서빙 데이터 …
Read Article