티스토리 뷰

본체험제품은 No1.전자부품 전문 쇼핑몰 아이씨뱅큐 의 지원을받아 작성한 것입니다.



제품명 : 아두이노 우노 스타터 키트 Pro E2 매뉴얼 LK-E2-BOOK


------------------------------------------


판매처 : LK임베디드ICbanq(구성품이 다름)



안녕하세요.

저는 이번 사용기로 IR리모컨을 선택했습니다.
사용기이기 때문에 최대한 Kit의 구성품들로 진행해보았습니다. 당연히 책자를 따라서 해보았구요.

하지만, 책자가 잘못되어 있었습니다.
다행히 습관상 데이터시트를 먼저 찾아보고 하기 때문에 상관은 없었습니다만...
Starter Kit라는 이름처럼, 처음하는 분들은 그대로 따라할 수 밖에 없다는 점에서 치명적이네요.
게시판을 보니 다른 분이 정오표를 찾아오긴 하셨지만, 판매하실 때는 수정본을 판매하시거나 정오표를 넣던지, 정오표를 확인해야한다는 명확한 고지를 해야한다고 생각합니다. Starter Kit니까요.





회로구성


회로 구성은 책자와 동일하게 진행하였습니다. (정확히는 정오표 수정대로입니다)










소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <IRremote.h>
 
int IR_RECV_PIN = 2;
IRrecv irrecv(IR_RECV_PIN);
decode_results results;
 
void setup() {
  Serial.begin(9600);
  irrecv.enableIRIn();
}
 
void loop() {
  if(irrecv.decode(&results))
  {
    if(results.decode_type == NEC)
    {
      switch(results.value)
      {
        case 0x00FF6897:
          Serial.println("Press '0'");
          break;
        case 0x00FF30CF:
          Serial.println("Press '1'");
          break;
        case 0x00FF18E7:
          Serial.println("Press '2'");
          break;
        case 0x00FF7A85:
          Serial.println("Press '3'");
          break;
        case 0x00FF10EF:
          Serial.println("Press '4'");
          break;
        case 0x00FF38C7:
          Serial.println("Press '5'");
          break;
        case 0x00FF5AA5:
          Serial.println("Press '6'");
          break;
        case 0x00FF42BD:
          Serial.println("Press '7'");
          break;
        case 0x00FF4AB5:
          Serial.println("Press '8'");
          break;                          
        case 0x00FF52AD:
          Serial.println("Press '9'");
          break;                          
        default:
          break;                                                      
      }
    }
    irrecv.resume();
  }
}
cs

소스도 마찬가지로, 책자와 동일합니다.

이제 Kit 안에 들어있는 리모컨을 사용하면, 0~9번 버튼에 맞게 Serial 창에서 데이터를 확인할 수 있습니다.
하지만, 아무리 사용기라고 해도 이렇게만 따라하면 재미 없잖아요?
이 Kit를 가지고 여러분이 추가로 해볼만한 내용을 가져와 보았습니다.








IR신호 해킹(Dump)


사실, 말이 해킹이지.. 그냥 Dump라고 생각하시면 됩니다. 그대로 읽어내서 뿌려보는것이죠.

IR에 대해 이해하면 더욱 편하시겠지만, 여기에 그 내용까지 올리면 너무 복잡하니, 후에 작성하여 링크 추가하도로 하겠습니다.
우리가 위에서 예제를 가지고 리모콘의 값이 제대로 인식되는지 확인해보았는데요.
실제 저 신호는 어떤 모습을 가지고 있을까요?
다행히, 예제에서 사용한 "IRremote" 라이브러리에는 이러한 예제가 존재합니다.
"IRrecvDumpV2" 라는 예제입니다.

우선 간단하게 이 예제의 원리에 대해 설명을 드리자면,
IR이라는 것도 결국은 어떠한 신호입니다. 디지털 입장에서 이 신호를 읽어들여서 나름대로의 프로토콜을 가지고 통신을 하는 것입니다. 기본적으로 Start 신호가 있으며, 그 뒤로 논리적 1과 0을 표현함으로써 다양한 신호를 만들어낼 수 있습니다.
따라서 이 예제에서는 이러한 신호들의 시간을 체크합니다.
가공하지 않은 신호들을 Raw 데이터라고 하며, 이것을 특정한 규칙에 따라 CheckSum 한 것을 Code 라고 합니다.

우리는 IR Receiver를 통해 Raw데이터를 알아내고, 반대로 이걸 활용하여 해당 리모컨을 사용하지 않더라도 IR컨트롤을 할 수 있겠죠. 이 예제에서는 다양한 IR코드에 대한 정보를 가지고 있으니 (주로 에어컨, TV, 오디오같은 것들이겠죠?), 해당 제품을 가지고 있으신 분들은 더욱 쉽게 접근할 수 있겠습니다.

코드는 아래와 같습니다 (기존 예제에서 핀 번호만 변경하였습니다 - Kit 예제에서 사용된 2번으로)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//------------------------------------------------------------------------------
// Include the IRremote library header
//
#include <IRremote.h>
 
//------------------------------------------------------------------------------
// Tell IRremote which Arduino pin is connected to the IR Receiver (TSOP4838)
//
int recvPin = 2;
IRrecv irrecv(recvPin);
 
//+=============================================================================
// Configure the Arduino
//
void  setup ( )
{
  Serial.begin(9600);   // Status message will be sent to PC at 9600 baud
  irrecv.enableIRIn();  // Start the receiver
}
 
//+=============================================================================
// Display IR code
//
void  ircode (decode_results *results)
{
  // Panasonic has an Address
  if (results->decode_type == PANASONIC) {
    Serial.print(results->address, HEX);
    Serial.print(":");
  }
 
  // Print Code
  Serial.print(results->value, HEX);
}
 
//+=============================================================================
// Display encoding type
//
void  encoding (decode_results *results)
{
  switch (results->decode_type) {
    default:
    case UNKNOWN:      Serial.print("UNKNOWN");       break ;
    case NEC:          Serial.print("NEC");           break ;
    case SONY:         Serial.print("SONY");          break ;
    case RC5:          Serial.print("RC5");           break ;
    case RC6:          Serial.print("RC6");           break ;
    case DISH:         Serial.print("DISH");          break ;
    case SHARP:        Serial.print("SHARP");         break ;
    case JVC:          Serial.print("JVC");           break ;
    case SANYO:        Serial.print("SANYO");         break ;
    case MITSUBISHI:   Serial.print("MITSUBISHI");    break ;
    case SAMSUNG:      Serial.print("SAMSUNG");       break ;
    case LG:           Serial.print("LG");            break ;
    case WHYNTER:      Serial.print("WHYNTER");       break ;
    case AIWA_RC_T501: Serial.print("AIWA_RC_T501");  break ;
    case PANASONIC:    Serial.print("PANASONIC");     break ;
    case DENON:        Serial.print("Denon");         break ;
  }
}
 
//+=============================================================================
// Dump out the decode_results structure.
//
void  dumpInfo (decode_results *results)
{
  // Check if the buffer overflowed
  if (results->overflow) {
    Serial.println("IR code too long. Edit IRremoteInt.h and increase RAWLEN");
    return;
  }
 
  // Show Encoding standard
  Serial.print("Encoding  : ");
  encoding(results);
  Serial.println("");
 
  // Show Code & length
  Serial.print("Code      : ");
  ircode(results);
  Serial.print(" (");
  Serial.print(results->bits, DEC);
  Serial.println(" bits)");
}
 
//+=============================================================================
// Dump out the decode_results structure.
//
void  dumpRaw (decode_results *results)
{
  // Print Raw data
  Serial.print("Timing[");
  Serial.print(results->rawlen-1, DEC);
  Serial.println("]: ");
 
  for (int i = 1;  i < results->rawlen;  i++) {
    unsigned long  x = results->rawbuf[i] * USECPERTICK;
    if (!(i & 1)) {  // even
      Serial.print("-");
      if (x < 1000)  Serial.print(" ") ;
      if (x < 100)   Serial.print(" ") ;
      Serial.print(x, DEC);
    } else {  // odd
      Serial.print("     ");
      Serial.print("+");
      if (x < 1000)  Serial.print(" ") ;
      if (x < 100)   Serial.print(" ") ;
      Serial.print(x, DEC);
      if (i < results->rawlen-1) Serial.print(", "); //',' not needed for last one
    }
    if (!(i % 8))  Serial.println("");
  }
  Serial.println("");                    // Newline
}
 
//+=============================================================================
// Dump out the decode_results structure.
//
void  dumpCode (decode_results *results)
{
  // Start declaration
  Serial.print("unsigned int  ");          // variable type
  Serial.print("rawData[");                // array name
  Serial.print(results->rawlen - 1, DEC);  // array size
  Serial.print("] = {");                   // Start declaration
 
  // Dump data
  for (int i = 1;  i < results->rawlen;  i++) {
    Serial.print(results->rawbuf[i] * USECPERTICK, DEC);
    if ( i < results->rawlen-1 ) Serial.print(","); // ',' not needed on last one
    if (!(i & 1))  Serial.print(" ");
  }
 
  // End declaration
  Serial.print("};");  // 
 
  // Comment
  Serial.print("  // ");
  encoding(results);
  Serial.print(" ");
  ircode(results);
 
  // Newline
  Serial.println("");
 
  // Now dump "known" codes
  if (results->decode_type != UNKNOWN) {
 
    // Some protocols have an address
    if (results->decode_type == PANASONIC) {
      Serial.print("unsigned int  addr = 0x");
      Serial.print(results->address, HEX);
      Serial.println(";");
    }
 
    // All protocols have data
    Serial.print("unsigned int  data = 0x");
    Serial.print(results->value, HEX);
    Serial.println(";");
  }
}
 
//+=============================================================================
// The repeating section of the code
//
void  loop ( )
{
  decode_results  results;        // Somewhere to store the results
 
  if (irrecv.decode(&results)) {  // Grab an IR code
    dumpInfo(&results);           // Output the results
    dumpRaw(&results);            // Output the results in RAW format
    dumpCode(&results);           // Output the results as source code
    Serial.println("");           // Blank line between entries
    irrecv.resume();              // Prepare for the next value
  }
}
cs

자, 이제 이 코드를 위에서 구성한 회로의 보드(Uno)에 업로드를 합니다.
그리고 나서 Kit에 있는 리모콘을 눌러보죠.


하하... 뭔가 귀찮은 내용들이 보이네요..
참고로 위의 내용은 [IRrecvdump.ino] example을 사용한 내용입니다.
위에 제공된 코드인 V2가 더 깔끔하게 나오니, 다르다고 이상하게 생각하지 마셔요 :)
일단 온갖 버튼을 다 눌러 보았습니다.

보시면, HEX로 된 6자리 코드가 보이군요.
그리고, NEC라는 표현이 보이는데, 이것은 Decode 타입을 뜻합니다.
결국은 IR도 하나의 프로토콜로 정하여 서로 통신할텐데 그 기본이 되는 약속들이 있겠죠?
그러한 타입들을 나타내줍니다. 예를 들어 LG TV 리모컨을 사용하시면 LG라고 나옵니다.
또한, Raw 라는 내용도 보이네요. IR 신호를 표현한 것이라고 생각하시면 됩니다.
각각의 숫자는 시간입니다. 양수는 HIGH상태의 시간, 음수(-)는 LOW상태의 시간이죠.
보시면 규칙이 보이실거에요. 처음 시작할 때의 대기 HIGH시간과 LOW시간은 깁니다.
그리고 그 다음부터는 HIGH시간은 대체로 비슷하며, LOW시간이 조금씩 다르죠.
HIGH시간은 신호 구분을 위한 것이며, LOW시간이 짧고 긴것을 판단하여 논리적 0, 1을 판단합니다.
자세한 내용은 나중에 링크로 다룰게요.

여기서,, 우리는 한단계 더 나아갈 수 있습니다.
우리가 일상적으로 사용하는 리모컨을 해킹해보자!







에어컨 리모컨 해킹


IR 리모컨 중에 쓸만한게 뭐가 있을까... 고민을 해보았습니다.
원격에서 키고 끄고를 하고 싶은것이라고 하면... 에어컨이 제일 떠오르더라구요.
물론 겨울이라 컨셉트가 어긋난 감은 있지만, 제 집에 IR로 컨트롤 하는 보일러는 없더라구요..
실제로 그런게 있는지도 궁금합니다.

무튼, 집에 있는 리모컨은 아래와 같은 LG리모컨입니다. 버튼이 얼마 없죠?

이미 회로구성도 되어있고, 아두이노 스케치도 올라가 있습니다.
이제 눌러보죠. (아래는 위에 제공된 코드를 사용했을 경우 입니다)


눌렀더니 위와 같은 시리얼 메시지가 나왔습니다.
일단 LG라고 나오구요.
여러가지 RAW데이터들이 보입니다. 이 RAW데이터 어딘가에는 에어컨 세팅에 대한 데이터들이 들어 있습니다.
이걸 알고 싶으면 분석을 해야겠죠? LG에서 프로토콜을 알려주지 않는 이상은요!
하지만 여기서는 그냥 넘어가겠습니다 :)
다음만 이해하면 됩니다. 0x8800909 이라는 코드는 "냉방" "바람약함" "상하바랑" "24도" "예약없음" 의 데이터가 포함된 코드라는 것을요 :)  적절히 Default로 지정해두면 온/오프만 하더라도 쓸만하겠죠?

아래는 IR신호가 잘 전달되지 않았을 경우의 Decode입니다.
보시면 UNKNOWN이죠? 물론 이 예제에 등록되지 않은 내용들은 전부 UNKNOWN으로 나옵니다.
하지만, 저처럼 LG라는게 명확히 나오는 경우가 있는 상황에서의 UNKNOWN은 문제가 있는 것이겠죠?
이러한 것들은 확인을 해주어야 합니다.
여담이지만, 우리가 간혹 리모컨을 누르는데 기기가 안먹는 경우가 있죠. 리모컨 화면은 혼자 꺼지구요...
이러한 경우가 아래와 같은 현상으로 인한 것임을 알 수가 있습니다.
IR 신호가 에어컨의 리시버에 제대로 전달되지 못한 것이죠!


영상으로 보시면...


이런... 벌써 글이 너무 길어졌네요.
일단 사용기이기때문에 기본적인 교재와 KIT 물품들을 활용해서 진행해보았습니다.
추가로 집에 돌아다니는 제품도 접목시켜보았구요.

다음 활용기에는
이 Decode한 데이터를 가지고 원격지에서 에어컨을 제어하는 내용을 다룰려고 합니다.
아마 단순함을 위해서 Default설정 값으로 ON/OFF만 클라우드 서비스를 활용하여 할 것 같습니다.
일단 하는김에 조도센서랑 온도센서도 하고,
LCD를 활용한 NTP 시간표현도 해볼까 생각은 하고 있습니다만...
일단 상황봐서 진행하도록 하겠습니다 :)

뭐.. 한겨울에 이런걸 하느냐.. 하실분들도 있지만,
일단... 여름대비(?) 차원에서, 미리 에어컨 제어장치를 만들어두는것에 의의를 두죠 :)!
실제로 여름에 집 들어오기전에 잠깐 켜두기만 하셔도 쾌적한 집이 되실겁니다 굳굳!







장/단점


[장점]

- 말그대로 입문자에게 좋습니다. 뭘 사야하는지 어떤 걸 사야하는지도 모르는 상황에서는 Kit 구입이 더 합리적입니다.

- 교재가 있어서, 방황하지 않을 수 있습니다. (정오표대로 고치기만 하면)

- 위 내용을 역으로 말하면 입문자 교육용으로 좋다는 겁니다.

- 회로를 하나하나 구성해보기때문에 회로구성에도 관심있는 사람에겐 유익합니다.
- 교재 내부에 QR코드가 있어 url입력이 귀찮으신 분들은, QR코드를 읽어 편하게 할 수 있습니다.


[단점]

- 책의 오류사항 / 정오표에 대한 별도 고지가 없음

  오탈자는 충분히 그럴수 있지만, 회로가 잘못된 경우는 타격이 큽니다.

  "위에서 입문자에게 좋다"라고 했지만 이런 부분이 해결되지 않으면 "입문자에게 혼란을 야기합니다"

  앞으로 판매할 제품들에 대해서는 정오표를 동봉해서 판매하거나 별도의 고지가 필요해 보입니다.

- 당연히 숙련자에게는 불필요한 구성 + 비싼 가격입니다.

  하지만 위의 장점에서도 언급했듯이 모든 것들은 적재적소의 특성을 가지고 있습니다

  자신이 어떠한 것을 원하는지에 따라 이 제품의 가치는 천차만별일 것입니다.







Spec





아두이노키트 구입처 아이씨뱅큐 http://www.icbanq.com/P007468190

아이씨뱅큐 무상체험단 카페 : http://cafe.naver.com/icbanq

아이씨뱅큐 공식 블로그  :http://blog.naver.com/icbanq



댓글
댓글쓰기 폼