커스텀 아두이노 TV 리모컨: 배터리 구동, 3D 프린트 케이스
프로젝트 배경
여자친구 TV 리모컨이 고장 났습니다. 근처 가게에서 싼 걸 사도 되지만, 둘 다 엔지니어라 '직접 만들어보자'고 의기투합했습니다. 서로 보완적인 기술로 록다운 기간에 재미있게 진행했고, 일상 기술의 작동 원리도 배웠습니다. 집에 소형 3D 프린터가 있던 것도 큰 도움이 되었습니다.
작동 원리
최소 명령 세트 정의
우리 TV 조작에 필요한 최소 명령 6개로 제한했습니다: 전원 토글, 입력 소스 전환, 채널 +/-, 볼륨 +/-.
버튼 읽기 (내부 풀업 활용)
추가 저항 없이 아두이노 내부 풀업 저항(INPUT_PULLUP)을 사용합니다. 슬립 모드가 아닐 때 매 루프마다 버튼 상태를 폴링하며, millis() 기반 디바운싱으로 중복 입력을 방지합니다.
적외선 신호 전송 (IRremote 라이브러리)
IRremote.h 라이브러리로 IR 통신을 처리합니다. 우리 TV 프로토콜에 맞춰 설정하고, 구글 검색으로 각 명령의 HEX 코드를 찾아 버튼에 매핑했습니다. 회로상 IR LED는 아두이노 우노 PWM 3번 핀에 연결해야 합니다. 현재 코드는 LG, 삼성, 소니 TV 3종을 #define으로 선택 가능하게 구성했습니다.
배터리 절약 전략 (avr/sleep.h, avr/power.h)
- 미사용 주변장치 비활성화: ADC, SPI, TWI(I2C), 타이머1, USART (setup에서
power_xxx_disable() 호출)
- 내장 LED(핀 13) 끄기:
analogWrite(13,0)
- 아두이노 우노 전원 LED 탈거 (납땜 제거, 비권장지만 배터리 수명 크게 향상)
- 자동 슬립 모드: 5초간 버튼 입력 없으면
SLEEP_MODE_PWR_DOWN 진입, 전원 버튼(인터럽트 핀 2)으로 웨이크업
하드웨어 제작
전자 회로 조립 (실드 형태)
아두이노 우노 헤더에 꽂히는 프로토타입 보드에 전체 회로를 구성했습니다. 버튼은 최종 리모컨 위치를 고려해 납땜하고, 하단에 47Ω 저항 2개를 병렬 연결해 IR LED 전류를 조정했습니다. 납땜 와이어로 버튼 핀과 IR LED를 메일 핀 헤더에 연결해, 아두이노 위에 끼우기만 하면 모든 연결이 완료되는 '실드' 형태로 만들었습니다.
인클로저 CAD/CAM & 3D 프린팅
- 설계: 솔리드웍스(SolidWorks)
- 슬라이싱: 울티메이커 큐라(Ultimaker Cura)
- 출력: 애니큐빅 메가 제로(Anycubic Mega Zero), PLA 필라멘트
- 구조: 베이스와 커버 2파트. 배터리, 아두이노+실드 조립체 수납. 상단에 버튼 6개 노출, 전면에 IR LED 홀.
- 아두이노 고정: 베이스 내부 돌출부에 M2.5 나사 3개로 고정
- IR LED 정렬: 프로토보드 밖으로 돌출된 LED가 전용 홀과 맞물리게 미니 포스트 3개로 측면 흔들림 방지
- 배터리: 전용 공간에 압입(Press-fit) 고정
- 커버: 버튼 심볼 각인, 베이스와 M3 나사 4개로 조립
포토 갤러리
완성품 외관, 내부 조립 상태, 3D 프린트 파트, 회로 기판 근접 사진 등 본문 이미지 참조.
완전 소스 코드 (TV_remote.ino)
#include
#include
#include
// 핀 정의
#define OnOff 1
#define Source 0
#define CH_plus 4
#define CH_minus 5
#define Vol_plus 6
#define Vol_minus 7
#define Interrupt_Pin 2 // 인터럽트 가능 핀 (우노: 2, 3)
// TV 모델 선택 (하나만 주석 해제)
//#define LG_TV
//#define SAMSUNG_TV
#define SONY_TV
// HEX 코드 정의 (각 모델별)
#ifdef LG_TV
unsigned long on_off_code = 551489775; // 0x20DF10EF
// ... 볼륨, 채널, 소스 코드
#endif
#ifdef SAMSUNG_TV
unsigned long on_off_code = 3772793023; // 0xE0E040BF
// ...
#endif
#ifdef SONY_TV
unsigned long on_off_code = 2704; // 0xA90
// ...
#endif
IRsend irsend;
unsigned long debounce_time, last_time;
void setup() {
analogWrite(13,0); // 내장 LED 끄기
pinMode(OnOff, INPUT_PULLUP);
pinMode(Source, INPUT_PULLUP);
pinMode(CH_plus, INPUT_PULLUP);
pinMode(CH_minus, INPUT_PULLUP);
pinMode(Vol_plus, INPUT_PULLUP);
pinMode(Vol_minus, INPUT_PULLUP);
pinMode(Interrupt_Pin, INPUT_PULLUP);
// 전력 절약: 미사용 주변장치 비활성화
power_adc_disable();
power_spi_disable();
power_timer1_disable();
power_usart0_disable();
power_twi_disable();
#ifdef DEBUG
Serial.begin(9600);
#endif
}
void loop() {
// 각 버튼별: 폴링 → 디바운스(40ms) → 확인 → IR 전송 → last_time 갱신
if (!digitalRead(OnOff)) { /* 디바운스 후 */ irsend.sendSony(on_off_code, 12); last_time = millis(); }
if (!digitalRead(Source)) { /* ... */ irsend.sendSony(source_code, 12); last_time = millis(); }
// CH_plus, CH_minus, Vol_plus, Vol_minus 동일 패턴
// 5초 무입력 시 슬립 진입
if (millis() - last_time > 5000) {
going_to_sleep();
}
}
void going_to_sleep() {
sleep_enable();
attachInterrupt(digitalPinToInterrupt(Interrupt_Pin), wake_up, LOW);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_cpu();
}
void wake_up() {
sleep_disable();
detachInterrupt(digitalPinToInterrupt(Interrupt_Pin));
}
회로도 및 3D 파일
- 회로도 이미지 및 프리칭 파일(
remote_sketch_r4f8a47oWX.fz) 제공
- 3D 프린트 파일은 별도 공유 예정 (CAD 섹션 참조)