메뉴 건너뛰기

XEDITION

아두이노

IR 리모콘 코드 따기

묵묵이 2017.02.10 15:43 조회 수 : 14

IR 리모콘 코드 따기  [https://makewhatever.wordpress.com/2013/03/19/%EB%B3%B8%EA%B2%A9-robot-%EC%A0%9C%EC%9E%91-%EC%82%BD%EC%A7%88%EA%B8%B0-2-ir-%EB%A6%AC%EB%AA%A8%EC%BD%98-%EC%BD%94%EB%93%9C-%EB%94%B0%EA%B8%B0/]

 

 

 

적외선 신호포멧은 NEC, SONY, RC5, RC6 등 여러가지가 있으나 대표적으로 NEC(아시아 태평양 연안), RC5(유럽 등) 포멧이 주로 이용됩니다. 삼성과 LG는 NEC포멧을 사용합니다.

 

NEC 포멧은 아래와 같은 배열로 구성되어야 합니다.

  Lead code

 

 Custom code

(8bit) 

 Custom code

(8bit)

  Data code

(8bit)

Data code

(반전된 8bit)

 

 

Lead code

- 신호가 시작됨을 알립니다.

삼성 : 4.5ms HIGH, 4.5ms LOW

LG : 9ms HIGH, 4.5ms LOW

 

Custom code

- 제조사와 제품별 구분용입니다.

16bit를 통째로 이용하거나 8bit와 반전된 8bit를 이용하기도 합니다.

 

Data code

- 데이타를 표시합니다

8bit 와 반전된 8bit를 이용합니다

 

포멧의 개념은 대충 이렇게 구성되는구나라고만 이해하면 됩니다.

 

 

 


// IR 리시버를 2번 포트에 연결해두었습니다.
#define IR_IN   2
 
// 코드 포맷입니다. 우리나라는 대부분 NEC.
#define NEC    1
#define AEHA   2
#define SONY   3
 
// 버퍼 사이즈 잡아놓고...
#define BUF_SIZE   (512)
 
void setup()  {
  // 핀모드 인풋
  pinMode( IR_IN , INPUT );
  Serial.begin(115200);
}
 
 
void loop() {
  unsigned long usec, nec_data = 0;
  unsigned int i, n, irOffTime, minTime, aveCnt = 0, aveAdd = 0;
  unsigned int irdata[ BUF_SIZE ], timeunit, leaderH, leaderL, datalen;
  unsigned int format = 0;
  boolean isvalid = true;
 
  // 신호가 감지될 때까지 기다립니다. 할 일이 없어서 do가 없습니다.
  while( digitalRead( IR_IN ) == HIGH );
 
  // LOW 감지되면 시작
  for( i=0; i < BUF_SIZE; ) {
 
    // 현재 시간을 한번 체크
    usec = micros();
    // LOW인 시간 동안 멈춰!
    while( digitalRead( IR_IN ) == LOW );
    // 신호 길이를 기록하고, 다음 신호 길이는 일단 0으로.
    irdata[  i] = micros() - usec;
    irdata[++i] = 0;
 
    // 같은 방식으로 다음 HIGH에 대해서도 체크합니다.
    usec = micros();
    while( digitalRead( IR_IN ) == HIGH ) {
      irOffTime = micros() - usec;
      // 65ms 이상 HIGH 이면 신호 끝난 것으로 인식.
      if( irOffTime > 65000 ) goto ir_exit;
    }
    // 끝난게 아니면 길이 기록하고 다음으로 넘어감.
    irdata[i++] = irOffTime;
  }
 
  // 신호가 끝난 상황이라면,
  ir_exit:
  // 신호 최소 시간을 찾아냄
  minTime = irdata[0];
  for( i=0; irdata[i]; i++) minTime = min( minTime, irdata[i]);
 
  // 최소 시간 1.5배의 평균으로 시간 단위 잡습니다.
  for( i=0; irdata[i]; i++) {
    if( minTime * 3 / 2 > irdata[i] ) { aveAdd += irdata[i] - minTime;  aveCnt++; }
  }
  timeunit = aveAdd / aveCnt + minTime;
 
  // 시간 단위가 너무 짧으면 이상하니까 리턴
  if( timeunit < 300 ) return;
 
  // 아니면 시간 단위로 나누어 irdata[]에 넣습니다.
  for( i=0; irdata[i]; i++) irdata[i] = ( irdata[i] + timeunit / 2 ) / timeunit;
 
  // 코드 시작하기 전 반복을 제거합니다.
  leaderH = irdata[0];
  leaderL = irdata[1];
  for( i=0;; i+=2) {
    irdata[i] = irdata[i+2];  irdata[i+1] = irdata[i+3];
    // 상태가 멈춘 곳까지 체크해서 데이터 길이를 얻어내고 브레이크!
    if( irdata[i+1] > 10 || irdata[i+1] == 0 ) { irdata[i+1] = 0;  datalen = i / 2 + 1;  break;  }
  }
 
  // 신호의 포맷을 알려줌. 이렇다네요. 이 부분은 깊게 알아보지는 않았습니다.
  Serial.print( "nnFormat           : ");
  if((leaderH > 14 || leaderH < 18) && leaderL == 8 ) { format = NEC;  Serial.println( "NEC"  ); }
  else if           ( leaderH == 8  && leaderL == 4 ) { format = AEHA; Serial.println( "AEHA" ); }
  else if           ( leaderH == 4  && leaderL == 1 ) { format = SONY; Serial.println( "SONY" ); }
  else  Serial.println( "???" );
 
  // 포맷에 따라
  switch( format ) {
    // NEC 포맷의 경우만
    case NEC:
      for( n=0; n < datalen - 1; n++) {
        nec_data |= ( (irdata[n*2+1]==1) ? 0UL : 1UL ) << n;
      }
      if( !isvalid ) break;
      Serial.print( "nCustom code      : 0x");
      Serial.print(  nec_data        % 256 , HEX);
      Serial.print( "nCustom code'     : 0x");
      Serial.print( (nec_data >>  8) % 256 , HEX);
      Serial.print( "nData code        : 0x");
      Serial.print( (nec_data >> 16) % 256 , HEX);
      Serial.print( "nData code (nega) : 0x");
      Serial.print( (nec_data >> 24) % 256 , HEX);
      break;
  }
}
 

위로