적외선 통신 (라이브러리 사용X, 직접 구현)
안녕하세요 이 글에서는 적외선통신을 라이브러리 없이 직접 구현해 보겠습니다.
사실상 특수한 경우를 제외하고는 라이브러리를 사용하는것이 좋습니다. 하지만 저는 특수한 경우이기에 직접 구현을 해야 했습니다 ㅠㅠ
일단 적외선 통신을 모르는 분은 없겠지만 TV리모컨이나 에어컨 리모컨에서 사용하는 통신입니다.
적외선 통신은 이와같이 실내에서 상대적으로 가까운 거리에서 통신을 하게 되는데 이유는 빛을 이용한 통신이기에
실외에서 특히 태양빛이 있는 곳에서는 통신이 거의 불가능하기 때문입니다. 또한 거리가 멀어지면 빛이 약해지기에
통신이 불안정해지는 단점들이 있지만 장점으로는 타 통신방식을 사용할때보다 소비전력이 작고 보조회로가 복잡하지
안기에 저렴하게 제작할 수 있어 가전제품을 무선으로 제어하기에 효율적인 통신 방식입니다.
이제 적외선 통신을 사용하기 앞서 원리를 알아보겠습니다.
적외선 통신은 빛을 발산하냐 안하냐 로 통신을 하게 되는데 이것만 보면 빛을내면 1 빛을 안내면 0 이라고 생각할 수 있지만(사실 제가 그렇게 생각했었습니다ㅋㅋ) 0110001011 이런식으로 으로 빛을 계속 발산하고 있기도 하고 발산을 안하기도 하기에 1과 0이 몇개인지 구분할수가 없습니다. 그래서 적외선 통신은 특수한 방식의 프로토콜을 사용해서 데이터를 보내게 됩니다.
위 그림은 적외선 송신을 하기위한 샘플파형 입니다. 처음에는 9ms시간동안 1을 출력하고 4.5ms동안 0을 출력하는데 이 부분이 데이터를 보내겠다고 수신부에 알리는 과정이라고 보시면 됩니다. 이후에 나오는 파형이 실제로 데이터를 보내는 파형이지만 앞에서 시작을 알리는 과정이 필요한 이유는 위에서 보시다시피 데이터가 보내지는 시간은 1ms 와 2ms 로 짧은 시간이기에 mcu가 이 신호를 다른 로직을 처리를 하는 과정에서 노칠 수 있기에 보다 긴 시간의 파형을 만들어서 준비하는 시간을 만들어 주는것입니다. (***참고로 이것은 저의 생각입니다.ㅋㅋ 100% 신뢰하지는 마세요!***)
그리고 데이터가 보내지는 파형을 보시면 1을 출력하는 부분은 동일하게 1ms이고 0을 출력하는 부분은 1ms 이기도 하고 2ms이기도 합니다. 짐작하신대로 1은 클럭이고 0이 데이터입니다. 1이 보내진 후에 0이 1ms동안만 출력된다면 보내지는 데이터는 0 이고 2ms 동안 출력된다면 보내지는 데이터는 1 입니다.
사실은 위 파형은 실제 적외선 통신 프로토콜 구조와는 많은 부분에서 다릅니다. 제가 이해하기 쉽고 보기 편하도록 조금 바꾼 프로토콜 입니다. 데이터가 보내지기 앞서 시작을 알리는 부분은 동일하지만 데이터가 보내지는 부분은 1ms 보다 짧고 4개의 영역으로 나뉘어 보내지게 됩니다. 하지만 실제 파형을 보면 이해하기가 힘들어지기도 하고 구현하는게 복잡하기에 제가 조금 변경한 이 파형으로 구현해 보겠습니다. 원리는 동일하기에 문제는 없습니다.
하지만! 이 파형을 그대로 적외선led로 출력하면 수신을 하지 못하게 됩니다.
그 이유는 주파수가 다르기 때문입니다.
저는 이 부분에서 삽질하여 2일 을 날렸습니다.ㅠㅠ
위 파형을 보시면 처음 보여드린 파형과 다른 빨간색 줄이 있습니다. 저 줄은 듀티비가 50% 인 38kHz 파형 입니다.
적외선이 1을 출력하는 부분에서 위 파형처럼 38kHz로 만들어서 출력시켜줘야 합니다.
이 주파수는 거의 대부분의 적외선 리모컨에서 동일하게 사용됩니다. 물론 꼭 38이 아닌 37.5 ~ 38.5 정도로 변동이
있어도 상관은 없지만 최대한 동일한 주파수로 해야 통신이 잘 됩니다.
위 이미지는 오실로스코프로 출력과 입력을 동시에 측정하여 캡쳐한 사진 입니다.
*위쪽 파형이 출릭파형이고 아래쪽이 입력 파형입니다.
입력쪽을 보시면 파형이 연결되서 들어오고 있는것을 볼수 있습니다. 또한 입출력이 서로 반전이 되서 들어오는데 수신센서의 특성상 반전이 되는것이기에 문제가 없습니다.
그러면 이제 이 파형을 적외선으로 출력시키기 위한 회로를 구성해보고 코드를 작성해 보겠습니다.
먼저 필요한 부품입니다.
번호 | 부품 | 이미지 | 수량 | 사용목적 |
1 | 아두이노(우노, 나노, 메가)등 상관없음 | ![]() |
2 | 송수신을 제어하기 위해 사용 |
2 | 적외선LED (940nm) *일반 LED와 동일하게 생겼다. |
![]() |
1 | 적외선 송신을 하기위해 필요 |
3 | 포토트렌지스터 | ![]() |
1 | 적외선 수신을 하기위해 필요 |
4 | 트렌지스터 npn(대표 1815) | ![]() |
1 | 적외선LED의 전류를 증폭하기 위해 사용 |
점퍼선과 브레드보드는 필수입니다!
적외선 송신부
위 회로는 정말 간단하게 적외선LED와 트랜지스터 로만 구성했지만 사실 트랜지스터가 없어도 동작은 합니다.
다만 안정성을 위해 연결한것입니다. 그리고 전압쪽에는 3.3v에 연결했지만 사실 5V에 연결해도 됩니다.(3.3V에 연결한다 헤도 베이스에서 인가되는 전압이 5V이기에 별로 의미가 없죠)
트랜지스터의 베이스에는 디지털2번 핀에 연결하였지만 디지털 핀 아무데나 연결해도 상관 없습니다.
적외선 수신부
위 회로가 적외선 수신부입니다. 수신회로는 더 심플하죠 ㅎㅎ
저 포토트랜지스터의 정격 전압이 2.7V ~ 5.5V 이기에 바로 5V로 연결해도 되지만 다이오드를 연결해서 0.7V전압강하를 만들어서 안전하게 하는것도 좋을거 같습니다.
신호를 입력받는 곳을 디지털2번 핀에 연결하였지만 디지털 핀 아무데나 연결해도 상관 없습니다.
다음은 소스코드를 작성해 보겠습니다.
참고로 코드는 정말 대충 작성하였습니다. (흔히말하는 스파게티코드 입니다...)
송신코드
bool data[4];
byte Signal_Pin = 2;
void setup() {
// put your setup code here, to run once:
pinMode(Signal_Pin, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
data[0] = false;
data[1] = false;
data[2] = false;
data[3] = false;
IR_remote(data);//데이터 전송
delay(5000);
data[0] = true;
data[1] = true;
data[2] = false;
data[3] = false;
IR_remote(data);//데이터 전송
delay(5000);
data[0] = false;
data[1] = false;
data[2] = true;
data[3] = true;
IR_remote(data);//데이터 전송
delay(5000);
data[0] = true;
data[1] = true;
data[2] = true;
data[3] = true;
IR_remote(data);//데이터 전송
delay(5000);
}
void IR_remote(bool data[]) {
for (int i = 0; i < (38 * 9) + 5; i++) { //9ms start signal
digitalWrite(Signal_Pin, true);
delayMicroseconds(11);
digitalWrite(Signal_Pin, false);
delayMicroseconds(11);
}
delayMicroseconds(4500);
for (int bit_shift = 0; bit_shift < 4; bit_shift++) {
for (int i = 0; i < 38; i++) { //1ms click
digitalWrite(Signal_Pin, true);
delayMicroseconds(11);
digitalWrite(Signal_Pin, false);
delayMicroseconds(11);
}
delay(data[bit_shift] + 1);//data_1
}
for (int i = 0; i < 38; i++) { //1ms click
digitalWrite(Signal_Pin, true);
delayMicroseconds(11);
digitalWrite(Signal_Pin, false);
delayMicroseconds(11);
}
delay(4);//finish
}
수신코드
char IR_Pin = 2;
void setup() {
// put your setup code here, to run once:
pinMode(IR_Pin, INPUT);
pinMode(13, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
int data = IR_Data(IR_Pin);
if (data == 12) {
digitalWrite(13, true);
} else {
digitalWrite(13, false);
}
}
int IR_Data(char pin) {
char time_count = 0;
char Data = 0;
char shift = 0;
if (digitalRead(pin) == false) { //9ms의 시작 신호 가 감지되면
while (true) {
//다음 4.5ms 의 true의 신호가 들어올때까지 반복
if (digitalRead(pin) == true) {
while (true) {
if (digitalRead(pin) == true) {
time_count++;
} else if (time_count == 5) {
// 신호가 4~5정도가 입력되면 데이터 입력 시작
time_count = 0;//카운트 리셋
while (true) { //실질적인 데이터 입력부
if (digitalRead(pin) == true) {
time_count++;
} else if (time_count == 2) { //2ms 가 들어오면
Data++;
if(shift != 4) Data = Data << 1;//1bit shift
shift++;
time_count = 0;
} else if (time_count = 1) { //1ms 가 들어오면
if(shift != 4) Data = Data << 1;//1bit shift
shift++;
time_count = 0;
}
if (shift >= 5) {
delay(1);
return Data;//입력이 끝나면 입력받은 값 반환
}
delay(1);
}
} else {
return -1;//start 입력신호가 아니라면 종료
}
delay(1);//1ms 간격으로 측정
}
}
}
}
}
제 코드를 블로그에 작성하니 창피하네요
이 코드는 기능만 구현되면 되지 라는느낌으로 작성된 코드라서 여러분이 더 예쁜 코드로 작성해서 사용하시는게 더 좋을수도 있습니다. 이런 더러운 코드를 보여드려 죄송합니다 ㅠㅠ
어디까지나 테스트용 코드라 더럽지만 동작은 합니다.
간단하게 동작을 설명드리면 5초 간격으로 0000, 0011, 1100, 1111 신호를 반복적으로 보내고 1100이라는 값이 수신되면 13번핀 led를 점등 다른 신호가 들어온다면 소등 시킵니다.
소스코드는 복사하셔도 되고 제 깃허브에서 다운받으셔도 됩니다.
사용하실 분이 계실지는 모르겠지만요 ㅋㅋ
https://github.com/DeveloperSungHyun/Arduino_Infrared-Data-Association
GitHub - DeveloperSungHyun/Arduino_Infrared-Data-Association: InfraredDataAssociation_project
InfraredDataAssociation_project. Contribute to DeveloperSungHyun/Arduino_Infrared-Data-Association development by creating an account on GitHub.
github.com
만약 더 좋은 코드를 작성하셨다면 공유 부탁드립니다!