마음 속 하늘은 항상 맑음~!

아두이노를 이용한 원격 리모트 컨트롤. 본문

하드웨어

아두이노를 이용한 원격 리모트 컨트롤.

파르셀수스 2014. 9. 22. 13:22

 갑작스레 원격 컨트롤 이야기가 나와서 만들게 된 원격 리모트컨트롤 모듈. 이더넷 보드인 enc28j60을 결합하고 적외선 LED를 3번에 연결하면 끝난다. 이더넷 모듈은 13번부터 8번까지를 사용하는데, 9번과 10번은 연결하지 않는다. 하드웨어는 비교적 간단하고 중요한 부분은 프로그래밍 부분이었다.


 일단 라이브러리를 구해야 하는데, 

https://github.com/shirriff/Arduino-IRremote

https://github.com/jcw/ethercard

에서 받을 수 있다.


주의할 점은 enc28j60은 전원이 3.3v라서 5v를 연결하면 큰일난다. 신호입력은 큰 문제가 없는 것 같고, 칩이 구동시에 약간 따끈한 정도로 아마 발열이 조금 있는 듯 싶다.


 IR Remote모듈의 수신은 이더넷 모듈과 같이 사용할 수 없다. 아마도 인터럽트쪽을 사용하는 것 같은데 그로인해서 기능상의 문제가 일어나는 듯 싶다.


 몇몇 스크래치 예제들을 통해서 코드를 구성하면 다음과 같다. 



/* ethercard and irDA remote control */

#include <EtherCard.h>
#include <IRremote.h>

//#define IR_ONLY
#ifndef IR_ONLY
// ethernet interface mac address
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x35 };
// ethernet interface ip address
static byte myip[] = { 192,168,1,200 };
// gateway ip address
static byte gwip[] = { 192,168,1,1 };

byte Ethernet::buffer[700];
static long timer;
BufferFiller bfill;
byte irdata[104];
#endif

bool success_dhcp=false;

IRsend irsend;
#define IR_NORECV
#ifndef IR_NORECV
int recv_pin=5;
IRrecv irrecv(recv_pin);
decode_results results;
#endif
long lg_on=0x20df10ef;

char* hextobyte(char *str,byte *ret)
{
  *ret=0;
  byte cnt=0;
  while((str!=NULL)&&(*str!=0)&&(*str>32)) {
    if((*str>='0')&&(*str<='9')) {
      *ret+=*str-'0';
    } else
    if((*str>='a')&&(*str<='f')) {
      *ret+=10+(*str-'a');
    } else
    if((*str>='A')&&(*str<='F')) {
      *ret+=10+(*str-'A');
    } else {
      return NULL;
    }
    str++;
    cnt++;
    if(cnt==2) break;
    if((*str!=0)&&(*str>32)) *ret<<=4;
  }
  return str;
}

void ethernet_setip() {
    #ifndef IR_ONLY
    if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
    Serial.println( "Failed to access Ethernet controller");
  
    #define STATIC_IP
    #ifndef STATIC_IP
    success_dhcp=ether.dhcpSetup();
    #endif
    if(success_dhcp) {
      ether.printIp("My IP: ", ether.myip);
      // ether.printIp("Netmask: ", ether.mymask);
      ether.printIp("GW IP: ", ether.gwip);
      ether.printIp("DNS IP: ", ether.dnsip);     
      Serial.println("success dhcp");
    } else {
       // set ip address 192.168.1.200
       ether.staticSetup(myip,gwip);
       success_dhcp=true;
       Serial.println("static ip address : 192.168.1.200");
    }
    #endif 
}

void setup () {
  Serial.begin(9600);
  #ifndef IR_NORECV
  irrecv.enableIRIn(); 
  #endif
 
  ethernet_setip();
  //irsend.sendNEC(lg_on,32);
}

const char http_OK[] PROGMEM =
    "HTTP/1.0 200 OK\r\n"
    "Content-Type: text/html\r\n"
    "Pragma: no-cache\r\n\r\n";

const char http_Found[] PROGMEM =
    "HTTP/1.0 302 Found\r\n"
    "Location: /\r\n\r\n";

const char http_Unauthorized[] PROGMEM =
    "HTTP/1.0 401 Unauthorized\r\n"
    "Content-Type: text/html\r\n\r\n"
    "<h1>401 Unauthorized</h1>";

void loop() {
   // working
    #ifndef IR_NORECV
    if(irrecv.decode(&results)) {
      if(results.decode_type==LG) Serial.println("LG");
      Serial.println(results.value,HEX);
      irrecv.resume();
    }
    #else
    word len=ether.packetReceive();
    word pos=ether.packetLoop(len);
    char* wpos;
    if(pos) {
      bfill=ether.tcpOffset();
      char *data=(char*)Ethernet::buffer+pos;
      Serial.println(data);
      if(strncmp("GET /",data,5)!=0) {
        bfill.emit_p(http_Unauthorized);       
      } else {
        data+=5;
        if(data[0]==' ') {
          bfill.emit_p(http_OK);
          bfill.emit_p(PSTR("Arduino IRRemote server"));
        } else
        if(NULL!=(wpos=strstr(data,"q?data="))) {         
          Serial.println("start parsing");
          //Serial.println(wpos);
          byte byte2;
          wpos+=7;
          wpos=hextobyte(wpos,&byte2); // type
          irdata[0]=byte2;
          wpos=hextobyte(wpos,&byte2);  // bits
          irdata[1]=byte2;
          wpos=hextobyte(wpos,&byte2);  // repeat
          irdata[2]=byte2;
          wpos=hextobyte(wpos,&byte2);  // hz lo
          irdata[3]=byte2;
          wpos=hextobyte(wpos,&byte2);  // hz hi
          irdata[4]=byte2;        
          byte dpos=5;
          while((wpos!=NULL)&&(*wpos!=0)&&(*wpos>32)) {
            wpos=hextobyte(wpos,&byte2);
            irdata[dpos]=byte2;
            dpos++;
            if(dpos>=100) break;
          }
          irdata[dpos]=0;
          Serial.println("finish parsing");
          // send data if valid.
          char codetype[11];
          if(dpos>5) {
            switch(irdata[0]) {
              case 1: irsend.sendNEC(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"NEC",10); break;
              case 2: irsend.sendSony(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"Sony",10); break;
              case 3: irsend.sendRC5(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"RC5",10); break;
              case 4: irsend.sendRC6(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"RC6",10); break;
              case 5: irsend.sendDISH(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"DISH",10); break;
              case 6: irsend.sendSharp(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"Sharp",10); break;
              case 7: irsend.sendPanasonic(*((int *)&irdata[3]),*((long*)&irdata[5])); strncpy(codetype,"Panasonic",10); break; // word address, long data
              case 8: irsend.sendJVC(*((long *)&irdata[5]),irdata[1],irdata[2]); strncpy(codetype,"JVC",10); break;          // long data, repeat
              case 9: irsend.sendSAMSUNG(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"SAMSUNG",10); break;
              default: irsend.sendRaw((unsigned int*)&irdata[5],irdata[2],*(unsigned int*)&irdata[3]); strncpy(codetype,"Raw",10); break;   // word* data, len, hz | hz, data1, data2, ... | len = repeat
            }
          }
          bfill.emit_p(http_OK);
          char temp[10];
          bfill.emit_p(PSTR("\nIRRemote parsed : $D / type:$S, bits:$D, repeat(len):$D, address(hz):$D, long code:$S\n"), dpos, codetype, irdata[1], irdata[2], *(unsigned int*)&irdata[3],ultoa(*((long *)&irdata[5]),temp,16));
          // dump
          for(int i=5; i<dpos; i++) {
            bfill.emit_p(PSTR("$H"),irdata[i]);
          }
        } else {
          bfill.emit_p(http_Unauthorized);
        }
      }
      ether.httpServerReply(bfill.position());
    }
    #endif
}



 고정적 IP와 dhcp를 사용할 수 있는데, 일단은 고정적인 IP로 했다. dhcp도 무리없이 작동했지만 자꾸 변하면 기기의 주소를 찾기를 힘들다 =ㅅ=; LG는 sendNEC를 사용하면 정상적으로 작동하는 것을 확인했다. 다른 기기는 아직 모르겠다. 문제는 기기들의 코드를 모른다는 것. 그래서 데이터를 몽땅 다 보내는 방식으로 처리. 브라우져를 이용하면 전송한 데이터를 받아볼 수 있다. GET방식으로 보내는데 앞의 5바이트까지의 16진수는 특정한 값을 가지며 첫번째는 기기의 종류를 선택할 수 있다.


 아직 더 테스트가 필요하지만 웹서버 기능이 완전하게 작동하는 것으로 일단 만족.