적외선 리모컨으로 가전제품과 전자 기기를 제어하는 것은 이제 일반적입니다. 하지만 같은 제어 작업을 더 쉽게 할 수 있습니다. 새로운 핸드 제스처 리모컨 시스템을 제안하는 주된 목적은 손에 든 리모컨을 들여다보며 특정 기능을 위한 특정 키를 찾을 필요를 없애는 것입니다. 특히 노년층에게 유용합니다. 이 프로젝트는 가전제품을 핸드 제스처로 제어하는 새로운 시스템을 제시합니다. 이 보고서에서는 이 프로젝트를 '플릭리모트(flickremote)'라고 부르겠습니다. 사용자의 제스처를 판단하기 위해 초음파 거리 측정기를 사용하고, 주어진 명령에 따라 IR 신호를 출력합니다. 마이크로컨트롤러 개발 보드 아두이노 101을 사용합니다.
하드웨어 구성요소
아두이노 101 또는 우노
HC-SR04 초음파 모듈
적외선 LED
적외선 LED용 적절한 저항 (220Ω)
RGB LED
브레드보드와 와이어
작동 원리:
이 프로젝트는 초음파 거리 측정기를 사용하여 사용자의 제스처를 판단하고, 주어진 명령에 따라 TV에 IR 신호를 출력합니다. 거리 또는 인치는 코드에서 편의에 맞게 수정할 수 있습니다.
높은 스와이프 (> 10인치) = 채널 업
낮은 스와이프 = 채널 다운
높은 홀드 (> 10인치) = 볼륨 업
낮은 홀드 = 볼륨 다운
센서 가리기 (< 3인치) = 켜기/끄기
회로도 (Fritzing 사용):
제작 방법:
IR LED를 적절한 저항을 통해 핀 3에 연결하고, 캐소드를 GND에 연결하세요.
초음파 센서를 5V와 GND에 연결하되 핀 사양에 맞게 연결하세요. 트리거 핀을 핀 8에, 에코 핀을 핀 7에 연결하세요. 이 핀들은 프로그램에서 설정 가능합니다.
RGB LED를 GND와 핀 11(빨강), 10(초록), 9(파랑)에 연결하세요. 이 핀들도 설정 가능합니다.
제어 방식
전원
3인치 이내에서 센터 위로 손을 스와이프하면 LED가 보라색으로 깜빡입니다. 이는 '켜기/끄기' 신호 전송 준비 확인입니다. 실수로 TV가 꺼지는 것을 방지하기 위해 첫 스와이프 후 5초 이내에 두 번째 스와이프를 기다리도록 했습니다. 이 시점에서 TV에 켜기/끄기 신호가 전송됩니다.
채널
센서로부터 10인치 이내(하지만 3인치 이내 아님)에서 스와이프하면 채널이 내려갑니다. 10~20인치 사이에서 스와이프하면 채널이 올라갑니다.
볼륨
센서로부터 10인치 이내(하지만 3인치 이내 아님)에서 손을 유지하면 볼륨이 내려갑니다. 10~20인치 사이에서 유지하면 볼륨이 올라갑니다. 손이 제거될 때까지 볼륨은 계속 변합니다(올리거나 내리거나).
장점 및 개선점:
손에 든 리모컨을 들여다보며 특정 기능을 위한 특정 키를 찾을 필요 없음
시각 장애인과 신체 장애인을 위한 가전제품(TV) 제어 솔루션
동일한 방법으로 모든 가전제품을 제어하도록 기능을 추가하여 프로젝트 개선 가능
감사합니다.
코드
코드 (C/C++)
/* 스와이프 리모컨 제어
이 스케치는 초음파 거리 측정기를 사용하여 사용자의 제스처를 판단하고,
주어진 명령에 따라 소니 TV에 IR 신호를 출력합니다.
- 높은 스와이프 (> 10인치) = 채널 업
- 낮은 스와이프 = 채널 다운
- 높은 홀드 (> 10인치) = 볼륨 업
- 낮은 홀드 = 볼륨 다운
- 센서 가리기 (< 3인치) = 켜기 / 끄기
Created by Vignesh Jaishankar
이 코드는 IRremote 라이브러리를 사용합니다 (https://github.com/shirriff/Arduino-IRremote)
*/
#include
// 제어 기능 정의
#define CONTROL_CH 1 // 채널 변경
#define CONTROL_VOL 2 // 볼륨
#define CONTROL_POW 3 // 전원
#define CONTROL_UP 1
#define CONTROL_DOWN -1
#define DIST_MAX 20 // 최대 거리(인치), 이 이상은 무시
#define DIST_DOWN 10 // 상/하 명령 임계값. 높으면 '업', 낮으면 '다운'
#define DIST_POW 3 // 전원 명령 임계값, 이하 = 켜기/끄기
// IR 핀
const int irPin = 3; // 라이브러리에서 정의됨, 이 변수는 상기용. 변경해도 라이브러리 핀은 안 바뀜
// 2핀 핑 센서
const int pingPin = 8;
const int echoPin = 7;
// 확인용 LED 핀
const int led = 13; // 업/다운 디버깅용 내부 LED
const int ledR = 11;
const int ledG = 10;
const int ledB = 9;
// LED 타이머
unsigned long timer;
// IR 송신 객체
IRsend irsend;
// 전원 확인 플래그 (신호 전송 위해 두 번 스와이프 필요)
boolean powerConfirmed = false;
void setup() {
Serial.begin(9600);
pinMode(led, OUTPUT);
pinMode(ledR, OUTPUT);
pinMode(ledG, OUTPUT);
pinMode(ledB, OUTPUT);
pinMode(pingPin, OUTPUT);
pinMode(echoPin, INPUT);
timer = millis();
}
void loop() {
long duration, inches;
int value;
// 읽기 확인
duration = doPing();
// 동작 확인용 타이머 (현재 전원만)
if (timer && timer < (millis() - 5000) && (millis() > 5000)) {
Serial.println("timer reset");
timer = false;
}
digitalWrite(led, LOW);
setColor(0, 0, 0); // 끄기
// 시간을 거리로 변환
inches = microsecondsToInches(duration);
// 최대 거리 이내면 작동
if (inches < DIST_MAX) {
// 디버그 출력
Serial.print(inches);
Serial.println("in");
// 매우 가까우면 '전원' 신호
if (inches < DIST_POW) {
Serial.println(timer);
// 켜기 또는 끄기
if (timer) {
doIR(CONTROL_POW, 0);
timer = false;
delay(2000); // 두 번 이상 전송 방지. 2초 지연
} else {
Serial.println("power flag set");
timer = millis();
setColor(255, 50, 50);
delay(500);
}
} else { // 볼륨 또는 채널
// 거리로 제어 방향 결정
value = handleDist(inches);
// 0.5초 대기
delay(300);
// 다시 확인, 손이 사라졌나?
if (microsecondsToInches(doPing()) > DIST_MAX) {
doIR(CONTROL_CH, value); // 스와이프
} else {
// 볼륨
int d = 500; // 첫 지연은 단일 볼륨 변경을 위해 더 김
// 손이 제거될 때까지 반복
while (inches < DIST_MAX) {
value = handleDist(inches); // 업 또는 다운?
doIR(CONTROL_VOL, value); // IR 신호 발사
delay(d); // 대기
inches = microsecondsToInches(doPing()); // 손 다시 확인
d = 100; // 빠른 다중 볼륨 조정을 위해 지연 단축
}
delay(500); // 볼륨 조정 후 실수로 채널 변경 방지
}
}
}
delay(50); // 모든 스와이프 감지하기에 충분히 짧음
}
/*
* 거리가 임계값 내면 '업'으로 표시하고 해당 LED 켬.
*/
int handleDist(int inches) {
if (inches > DIST_DOWN) {
digitalWrite(led, HIGH);
return CONTROL_UP;
} else {
digitalWrite(led, LOW);
return CONTROL_DOWN;
}
}
/*
* 올바른 IR 코드 발사
*/
void doIR(int control, int val) {
switch(control) {
case CONTROL_POW: // 전원
Serial.println("power on / off 0xa90");
for (int i = 0; i < 3; i++) {
setColor(255, 0, 0);
irsend.sendSony(0xa90, 12); // 소니 TV 전원 코드
delay(40);
}
break;
case CONTROL_CH:
setColor(0, 255, 0); // val에 따라 '채널 업/다운' 출력
if (val == CONTROL_UP) {
digitalWrite(led, HIGH);
for (int i = 0; i < 3; i++) {
irsend.sendSony(0x90, 12);
delay(40);
}
Serial.println("channel up 0xD00A");
} else { // 다운
for (int i = 0; i < 3; i++) {
irsend.sendSony(0x890, 12);
delay(40);
}
Serial.println("channel down 0x3002");
}
break;
case CONTROL_VOL:
setColor(0, 0, 255); // val에 따라 '볼륨 업/다운' 출력
if (val == CONTROL_UP) {
digitalWrite(led, HIGH);
for (int i = 0; i < 3; i++) {
irsend.sendSony(0x490, 12);
delay(40);
}
Serial.println("volume up 0x490");
} else { // 다운
for (int i = 0; i < 3; i++) {
irsend.sendSony(0xC90, 12);
delay(40);
}
Serial.println("volume down 0xC90");
}
break;
}
}
void setColor(int red, int green, int blue) {
analogWrite(ledR, red);
analogWrite(ledG, green);
analogWrite(ledB, blue);
}
long doPing() {
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
return pulseIn(echoPin, HIGH);
}
long microsecondsToInches(long microseconds) {
return microseconds / 74 / 2;
}
long microsecondsToCentimeters(long microseconds) {
return microseconds / 29 / 2;
}