중급 예제

약간은 익숙하시거나 익숙해지셨나요? 그렇다면 조금더 깊이 다뤄볼까요?

이더넷 쉴드 x RGB LED - 웹서버 구축하기

2014-11-20 09:29:18

개요

아두이노와 인터넷을 연결해서 사용할 수 있다면
아두이노를 활용할 수 있는 방법은 더욱 더 많아질 것입니다.
 
이더넷 모듈이나 쉴드와 접근가능한 IP가 있다면 쉽게 인터넷으로 통신을 할 수 있습니다.
 
출처 : www.embedds.com
 
출처 : arduinoelectronics.wordpress.com
 
위 사진 처럼 웹페이지를 통해 아두이노의 LED색을 바꾸거나 ON/OFF를 할 수도 있습니다.
 
출처 : blog.paraimpu.com
 
트위터를 연동할 경우도 대부분 이더넷을 사용합니다.
 

 

본문에서는 이더넷 쉴드를 사용하여 아두이노를 웹서버로 만들어 보겠습니다.
접속한 웹페이지에서 RGB 값을 선택해서 불빛을 제어 해보겠습니다.


 

미리보기 동영상

 

 

 

부품목록

NO 부품명 수량 상세설명
1 아두이노 mega ADK 1 모듈을 이용하시거나
다른 쉴드를 사용하시면 uno를 사용해도 됩니다.
2 브레드 보드 1  
3 이더넷 쉴드 1 WIZ550io 모듈이 부착된
ioShield-A를 사용했습니다.
4 점퍼 케이블 4  
5 RGB LED 1  
6 330 ohm 저항 3  

 

 

부품명 아두이노 mega ADK 브레드 보드 점퍼 케이블 RGB LED 330 ohm 저항
사진 X1 x1 x1 x1 x3

 

 

 

 

부품명 이더넷 쉴드
사진 X1

 

 

하드웨어 Making

회로도

브레드보드 레이아웃

소프트웨어 Coding

아두이노에 다음 스케치를 업로드하세요.
 

/*
 이더넷 쉴드를 이용한 웹서버 만들기 스케치입니다.

 created 18 Dec 2009
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe
 modified 15 Nov 2014
 by Soohwan Kim 

 본 스케치는 위즈네트 아카데미를 참고하였습니다.
 http://wiznetacademy.com/
 */

#include <SPI.h>
#include <Ethernet.h>

#if defined(WIZ550io_WITH_MACADDRESS) //WIZ550io에 표시된 MAC 주소를 사용한다면
;
#else
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; //mac 주소 입력
#endif

IPAddress ip(192,168,1,7);  //로컬 네트워크에서 유일한 IP를 사용해야 합니다.
IPAddress gateway( 192, 168, 1, 1 );
IPAddress subnet( 255, 255, 255, 0 );
// fill in your Domain Name Server address here:
IPAddress myDns(8, 8, 8, 8); // google puble dns

EthernetServer server(80); //80은 포트번호
void check_led_status();

#define RED_PORT 46     //RED rgb 핀
#define GREEN_PORT 44   //GREEN rgb 핀
#define BLUE_PORT 42    //BLUE rgb 핀

void setup() {
  Serial.begin(9600);
   while (!Serial) {
    ; //시리얼 포트가 연결 될 때까지 기다림, 레오나르도 경우만 필요
  }

  pinMode(RED_PORT, OUTPUT);    //RED 핀 출력 설정
  digitalWrite(RED_PORT, HIGH); //RED 핀 초기 설정으로 켜놓기
  
  pinMode(GREEN_PORT, OUTPUT);  //GREEN 핀 출력 설정
  pinMode(BLUE_PORT, OUTPUT);   //BLUE 핀 출력 설정

//이더넷 디바이스 초기화
#if defined __USE_DHCP__
#if defined(WIZ550io_WITH_MACADDRESS) //WIZ550io에 할당된 맥 주소를 사용할 경우
  Ethernet.begin();
#else
  Ethernet.begin(mac);
#endif  
#else
#if defined(WIZ550io_WITH_MACADDRESS) 
  Ethernet.begin(ip, myDns, gateway, subnet);
#else
  Ethernet.begin(mac, ip, myDns, gateway, subnet);
#endif  
#endif 

  //서버 연결 시작
  server.begin();
  Serial.println("WebServerControlLED");
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
}

void loop() {
  
  //수신 클라이언트가 있는 지 확인
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    //http 요청이 빈 라인으로 끝났을 때 
    boolean currentLineIsBlank = true;
      String buffer = ""; //버퍼 선언
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
		buffer += c;    //버퍼 할당
        Serial.write(c);
        
        if (c == '\n' && currentLineIsBlank) {
          //표준 http 응답 헤더 전송 시작
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          
          //본문(상황에 맞는 웹 페이지) 전달
          //client.println("<!DOCTYPE HTML>");, HTML5 사용시
          client.println("<html>");  //웹 페이지 작성은 HTML사용
          client.println("<body>");

          //LED에 상황을 판단한후 상태를 알림
	  if (digitalRead(RED_PORT)>0) {   //빨간 불이면
            client.println("LED is <font color='red'>RED</font>"); //웹페이지에 LED is RED라고 표시, RED는 빨간색
          } else if (digitalRead(GREEN_PORT)>0) { 
            client.println("LED is <font color='green'>GREEN</font>");
          } else if (digitalRead(BLUE_PORT)>0) {
            client.println("LED is <font color='blue'>BLUE</font>");
          } else {
            client.println("LED is <font color='black'>OFF</font>");
          }

          //색 선택 부분 만들기
          client.println("<br />");
          client.println("<FORM method=\"get\" action=\"/led.cgi\">");
          client.println("<P> <INPUT type=\"radio\" name=\"status\" value=\"1\">RED");
          client.println("<P> <INPUT type=\"radio\" name=\"status\" value=\"2\">GREEN");
          client.println("<P> <INPUT type=\"radio\" name=\"status\" value=\"3\">BLUE");
          client.println("<P> <INPUT type=\"radio\" name=\"status\" value=\"0\">OFF");
          client.println("<P> <INPUT type=\"submit\" value=\"Submit\"> </FORM>");

          client.println("</body>");
          client.println("</html>");
          break;
        }
        if (c == '\n') {          
          currentLineIsBlank = true;
		  buffer="";
        } 
        else if ( c == '\r') {
          //빨간 불을 선택하면
	  if(buffer.indexOf("GET /led.cgi?status=1")>=0){
	    //빨간 불 킴
	    digitalWrite(RED_PORT, HIGH);
            digitalWrite(GREEN_PORT, LOW);
            digitalWrite(BLUE_PORT, LOW);

            //두번째 페이지 전송
	    client.println("HTTP/1.1 200 OK");
	    client.println("Content-Type: text/html");
	    client.println();
	    client.println("<html>");
	    client.println("<body>");

	    if (digitalRead(RED_PORT)>0) {
              client.println("LED is <font color='red'>RED</font>");
            } else if (digitalRead(GREEN_PORT)>0) { 
              client.println("LED is <font color='green'>GREEN</font>");
            } else if (digitalRead(BLUE_PORT)>0) {
              client.println("LED is <font color='blue'>BLUE</font>");
            } else {
              client.println("LED is <font color='black'>OFF</font>");
            }

            client.println("<br />");
	    client.println("<a href=\"/led.htm\">Go to control-page</a>"); //다시 처음 페이지로 

	    client.println("</body>");
	    client.println("</html>");
	    currentLineIsBlank = false;
	    break;
	   }

           //초록을 선택하면
           if(buffer.indexOf("GET /led.cgi?status=2")>=0){
	     //초록 핀 킴
	     digitalWrite(RED_PORT, LOW);
             digitalWrite(GREEN_PORT, HIGH);
             digitalWrite(BLUE_PORT, LOW);
			
             client.println("HTTP/1.1 200 OK");
             client.println("Content-Type: text/html");
	     client.println();
	     client.println("<html>");
	     client.println("<body>");
               
	     if (digitalRead(RED_PORT)>0) {
               client.println("LED is <font color='red'>RED</font>");
             } else if (digitalRead(GREEN_PORT)>0) { 
               client.println("LED is <font color='green'>GREEN</font>");
             } else if (digitalRead(BLUE_PORT)>0) {
               client.println("LED is <font color='blue'>BLUE</font>");
             } else {
               client.println("LED is <font color='black'>OFF</font>");
             }

	     client.println("<br />");
	     client.println("<a href=\"/led.htm\">Go to control-page</a>");

	     client.println("</body>");
	     client.println("</html>");
	     currentLineIsBlank = false;
	     break;
	    }

            //파란 색을 선택하면
            if(buffer.indexOf("GET /led.cgi?status=3")>=0){
	      digitalWrite(RED_PORT, LOW);
              digitalWrite(GREEN_PORT, LOW);
              digitalWrite(BLUE_PORT, HIGH);
				
	      client.println("HTTP/1.1 200 OK");
	      client.println("Content-Type: text/html");
	      client.println();
	      client.println("<html>");
	      client.println("<body>");
               
	      if (digitalRead(RED_PORT)>0) {
                client.println("LED is <font color='red'>RED</font>");
              } else if (digitalRead(GREEN_PORT)>0) { 
                client.println("LED is <font color='green'>GREEN</font>");
              } else if (digitalRead(BLUE_PORT)>0) {
                client.println("LED is <font color='blue'>BLUE</font>");
              } else {
                client.println("LED is <font color='black'>OFF</font>");
              }

	      client.println("<br />");
	      client.println("<a href=\"/led.htm\">Go to control-page</a>");

	      client.println("</body>");
	      client.println("</html>");
	      currentLineIsBlank = false;
	      break;
	    }

            //off를 선택했을 때
	    if(buffer.indexOf("GET /led.cgi?status=0")>=0){
	      digitalWrite(RED_PORT ,LOW);
              digitalWrite(GREEN_PORT, LOW);
              digitalWrite(BLUE_PORT, LOW);
				
	      client.println("HTTP/1.1 200 OK");
	      client.println("Content-Type: text/html");
   	      client.println();
	      client.println("<html>");
	      client.println("<body>");
                
	      if (digitalRead(RED_PORT)>0) {
                client.println("LED is <font color='red'>RED</font>");
              } else if (digitalRead(GREEN_PORT)>0) { 
                client.println("LED is <font color='green'>GREEN</font>");
              } else if (digitalRead(BLUE_PORT)>0) {
                client.println("LED is <font color='blue'>BLUE</font>");
              } else {
                client.println("LED is <font color='black'>OFF</font>");
              }

	      client.println("<br />");
	      client.println("<a href=\"/led.htm\">Go to control-page</a>");

	      client.println("</body>");
	      client.println("</html>");
	      currentLineIsBlank = false;
	      break;
	    }
	} else{ //if( c != '\r') {

          currentLineIsBlank = false;
        }
      }
    }
   
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disonnected");
  }
}

소프트웨어 및 하드웨 설명

소프트웨어 설명

본 스케치를 이해하기 전에 먼저 몇 가지 용어에 대해서 설명하겠습니다.
 

출처 : http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Network_Programing/Documents/IntroTCPIP

인터넷은 TCP/IP 프로토콜 기반의 네트워크입니다.
프로토콜이라는 것은 약속입니다. 사람들 사이에서 대화를 할 때 각 단어가 의미하는 뜻을 정해놓은 것처럼
컴퓨터 사이의 인터넷을 통해서 통신을 하기 위해서 정해놓은 약속입니다.
일종에 어떤 규약을 정해놓고 그 규약을 지키면서 메세지를 보내고 해석하는 것입니다.
 
아두이노와 이더넷 쉴드를 사용하기 위해서 알아야하는 것은 
이더넷을 사용하여 통신할 때 TCP/IP 프로토콜 기반으로 하기 때문에 
몇 가지 초기 설정에 관련된 용어들에 대해서 알아야 합니다.
 
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; //mac 주소 입력

1. MAC address
: 물리적인 네트워크에서 TCP/IP 프로토콜을 사용시 각 기기를 구별할 수 있는 고유한 주소가 필요합니다.
바로 이 주소가 MAC address 입니다.
사람마다 주민번호가 있는 것과 같은 것 입니다.
자세히 모르더라도 중요한 것은 아두이노 스케치에서 이더넷 쉴드에 적힌 맥주소로 지정해줘야 합니다.
 
IPAddress ip(192,168,1,7);  //로컬 네트워크에서 유일한 IP를 사용해야 합니다.
IPAddress gateway( 192, 168, 1, 1 );
IPAddress subnet( 255, 255, 255, 0 );
// fill in your Domain Name Server address here:
IPAddress myDns(8, 8, 8, 8); // google puble dns

2. IP address
: 네트워크에 상에서 host를 구별하기 위한 수입니다.
스케치 내부에서 ip 주소를 할당해 주어야 합니다.
고유한 번호를 사용하셔야 하기 때문에 만약 충돌이 된다면 맨 뒤에 수를 바꿔보세요.

3. gateway 
: 다른 네트워크 host와 통신시 중간에 gateway를 거쳐야 합니다.

4. subnet
: 현재 네트워크가 자신이 속한 네트워크인지 아닌지 구분하기 위해 사용됩니다.

5. dns
: 사람들이 IP주소를 기억하기 쉽게 바꾼 주소를 의미합니다.
ex) www.naver.com

6.dns server address
: dns가 dns server를 통하여 ip 주소로 변환되는 데, 이때 dns server의 주소를 의미합니다.

중요한 것은 IP 주소만 주의해주시고 나머지는 그대로 입력하시면 됩니다.
 
출처 : adept.egloos.com
 
웹 서버를 만들기 전에 먼저 HTTP 프로토콜을 사용하는 서버-클라이언트 구조를 설명하겠습니다.
만약 크롬과 같은 웹 브라우저(클라이언트)의 주소창에서 kocoafab.cc 주소를 입력하면 
주소 등이 요청 메세지로 웹 서버로 전달 됩니다. 
여기서 웹서버는 웹사이트를 관리하고 있는 서버로 웹서버에 저장되어 있는 웹 페이지에 대한 
요청이 오면 해당 웹페이지 즉 네이터 메인 페이지를 응답 메세지로 보내줍니다.
여기서 서로 메세지를 만들고 받은 메세지를 해석할 때 HTTP 프로토콜을 사용합니다.

아두이노 스케치 내부에서 요청 메세지를 받거나 응답메시지를 만들 때 HTTP 프로토콜 형태에 맞춰서
메시지를 작성해야 합니다.
따로 공부하실 필요없이 위 스케치를 참고해서 어떤 형태인지 확인하셔도 무방합니다.

또한 아두이노를 웹 서버로 사용할 것이기 때문에 해당 요청이 왔을 때마다 제공해야 할 웹페이지를 작성해야 합니다.
본 스케치에서는 웹 페이지를 간단한 HTML로 작성했습니다.
스케치를 응용해서 다른 페이지를 제공하고 싶으시면 HTML를 공부하셔서 응용해보세요.

이제 스케치 설명을 시작하겠습니다.
 
#include <SPI.h>
#include <Ethernet.h>

본 스케치를 사용하기 위해서는 위의 두 개의 라이브러리가 필요합니다.
모두 스케치 기본 내장 라이브러리입니다.
하지만 WIZ550io를 사용하신다면 제조사에서 제공되는 라이브러리로 Ethernet 라이브러리를 대체하셔야 합니다.
라이브러리 다운로드는 다음 링크를 참고하세요.
주의할 점은 각 아두이노 스케치 버전에 맞는 라이브러리를 사용하셔야 합니다.
1.5.x / 1.0.x 버전을 확인하세요.
라이브러리 설치는 다음 링크를 참고하세요.
 
  //수신 클라이언트가 있는 지 확인
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    //http 요청이 빈 라인으로 끝났을 때 
    boolean currentLineIsBlank = true;
      String buffer = ""; //버퍼 선언
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
		buffer += c;    //버퍼 할당
        Serial.write(c);
        
        //요청 메세지가 다 전달되면
        if (c == '\n' && currentLineIsBlank) {
          //표준 http 응답 헤더 전송 시작
          client.println("HTTP/1.1 200 OK");

클라이언트가 요청이 있으면 버퍼에 메세지를 저장합니다.
만약 다음 행(\n) 문자를 받고 다음 행이 비어있음이 확인되면 메세지가 다 전달 된 것입니다.
이런 약속이 바로 HTTP 프로토콜입니다.

이제 응답 메세지의 헤더를 전송하기 시작합니다.
 
          //본문(상황에 맞는 웹 페이지) 전달
          //client.println("<!DOCTYPE HTML>");, HTML5 사용시
          client.println("<html>");  //웹 페이지 작성은 HTML사용
          client.println("<body>");

          //LED에 상황을 판단한후 상태를 알림
	  if (digitalRead(RED_PORT)>0) {   //빨간 불이면
            client.println("LED is <font color='red'>RED</font>"); //웹페이지에 LED is RED라고 표시, RED는 빨간색
          } else if (digitalRead(GREEN_PORT)>0) { 
            client.println("LED is <font color='green'>GREEN</font>");
          } else if (digitalRead(BLUE_PORT)>0) {
            client.println("LED is <font color='blue'>BLUE</font>");
          } else {
            client.println("LED is <font color='black'>OFF</font>");
          }

메세지의 본문은 웹페이지를 전하는 부분입니다.
본 스케치에서는 스케치 내부에서 HTML을 사용하여 웹페이지를 작성하면서 보내고 있지만
보통은 따로 작성한 후 보냅니다.

 총 2페이지를 작성할 것입니다.
하나는 현재 불 색깔을 표현하고 다른 색을 선택할 수 있는 페이지와
또 하나는 색을 선택한 후 다음 페이지로 성공했다고 보여주는 페이지 입니다.

HTML은 <태그 >를 사용하는 언어 입니다.
"LED is <font color='red'>RED</font>"이 부분이 HTML을 사용한 부분입니다.
font color라는 태그를 이용해서 RED 부분만 빨간색으로 표시하게 합니다.

초기 값으로 LED가 빨간불을 키도록 설정해 놨습니다.
그러므로 맨 처음 페이지 방문시 
웹페이지 맨 위에 LED is RED 라고 표시될 것입니다.

만약 선택 페이지에서 초록을 선택한 이후 다시 이페이지로 돌아올 경우
LED is GREEN으로 표시될 것입니다.
 
          //색 선택 부분 만들기
          client.println("<br />");
          client.println("<FORM method=\"get\" action=\"/led.cgi\">");
          client.println("<P> <INPUT type=\"radio\" name=\"status\" value=\"1\">RED");
          client.println("<P> <INPUT type=\"radio\" name=\"status\" value=\"2\">GREEN");
          client.println("<P> <INPUT type=\"radio\" name=\"status\" value=\"3\">BLUE");
          client.println("<P> <INPUT type=\"radio\" name=\"status\" value=\"0\">OFF");
          client.println("<P> <INPUT type=\"submit\" value=\"Submit\"> </FORM>");

          client.println("</body>");
          client.println("</html>");
          break;
        }

다음은 색을 선택하는 부분을 만드는 코드입니다.
<FORM>태그는 입력된 정보를 한번에 보낼때 사용합니다.
<FORM>태그 안을 보시면 action=\"/led.cgi\이 보이실 텐데요.
색을 선택 후 submit버튼을 누르면 웹 서버로 요청이 보내집니다.

이때 뒤 action 속성은 요청 메세지가 GET /led.cgi?status=1과 같이 보낼 때 요청 페이지를 지정하기 위한 속성입니다.

<INPUT type>에서 속성으로 radio를 선택하면 라디오 버튼이 만들어 집니다.
HTML을 이용해서 페이지를 만들 수 있다는 말이 어떤 것이지 감이 오나요?
만약 HTML에 대해 더욱 공부해보고 싶으신 분은 http://www.w3schools.com/html/라는 사이트를 추천해 드립니다.
 
          //빨간 불을 선택하면
	  if(buffer.indexOf("GET /led.cgi?status=1")>=0){
	    //빨간 불 킴
	    digitalWrite(RED_PORT, HIGH);
            digitalWrite(GREEN_PORT, LOW);
            digitalWrite(BLUE_PORT, LOW);

            //두번째 페이지 전송
	    client.println("HTTP/1.1 200 OK");
	    client.println("Content-Type: text/html");
	    client.println();
	    client.println("<html>");
	    client.println("<body>");

	    if (digitalRead(RED_PORT)>0) {
              client.println("LED is <font color='red'>RED</font>");
            } else if (digitalRead(GREEN_PORT)>0) { 
              client.println("LED is <font color='green'>GREEN</font>");
            } else if (digitalRead(BLUE_PORT)>0) {
              client.println("LED is <font color='blue'>BLUE</font>");
            } else {
              client.println("LED is <font color='black'>OFF</font>");
            }

            client.println("<br />");
	    client.println("<a href=\"/led.htm\">Go to control-page</a>"); //다시 처음 페이지로 

	    client.println("</body>");
	    client.println("</html>");
	    currentLineIsBlank = false;
	    break;
	   }

빨간 색을 선택하고 submit을 클릭하면
GET /led.cgi?status=1란 요청 메세지가 갑니다.

즉, 스케치에서는 이 요청메세지가 오면 빨간 핀을 HIGH로 하고
다음 페이지를 넘겨주면 됩니다.


 

kocoafabeditor

항상 진취적이고, 새로운 것을 추구하는 코코아팹 에디터입니다!

이더넷쉴드, 아두이노, mega ADK

은댕이 2014-11-25 09:31:25

안녕하세요~ 설명이 너무 잘되있어서 한눈에 들어옵니다.
자사 제품은 ioShield-A를 사용하여 구현해 주셨네요^^
현재 저희 회사에서 자사제품을 이용한 글들을 수집하고 있습니다. 귀사의 글을 공유해도 될런지요?
수집해 놓은 글을 모아놓은 사이트입니다
http://wiznetmuseum.com/
시간이 되시면 방문 부탁드립니다~

kocoafab 2014-11-25 11:39:49

네 공유해도 됩니다. 링크 걸어주신 사이트에도 굉장히 좋은 내용 많이 있네요:) 응용해서 다른 컨텐츠도 올리겠습니다.

이준희 2015-11-08 17:00:32

안녕하세요~ 여쭤보고 싶은게 있습니다.
현제 공유기를 통하여 WerServer예제는 해보았습니다. 위의 예제를 하면서 공유기 없이 고정 ip를 이더넷 쉴드와 연결하여 공유기에 의존하지 않고 기존의 네트워크 망을 통하여 데이터를 송수신 할려고 하는데 그 방법은 없나요??
만약 Lan 선이 211,150,40,30 일때 이 랜선을 이더넷 쉴드에 접속하여 아두이노 WebServer 의 ip를 211,150,40,30을 하여 데이터 송수신을 해보았지만 안되서 여쭤드립니다.

차지훈 2016-07-20 13:10:33

질문있습니다!!
현재 이 웹서버는 구현완성했는데...
여기 웹서버에서 실시간으로 센서값이 이동됩니다!! 근데 이 센서 값만 APP에서도 띄우고 싶은데 어떻게 해야하나요?(구현한 웹서버는 말고요!! 단순 센서값!!)json파일 이런거는 무엇인가요?