사물인터넷(IoT)2016. 3. 28. 16:48

아두이노를 인터넷에 연결하기 위한 방법으로는 예전에 소개했던 ESP8266 와이파이 모듈을 이용한 방법도 있으나 랜선 연결을 통하여 유선으로 인터넷에 연결하는 방법도 있다. 바로 ENC28J60 이라는 모듈이다. 현재 인터넷에서 만원 이하의 가격으로 구할 수 있는 기존 아두이노 이더넷 쉴드 대비 훨씬 저렴한 인터넷 연결 모듈이다. 


연결 단자는 총 10개가 있으며 각각의 용도는 위와 같다.


ENC28J60 모듈의 뒷면


■ ENC28J60 Data Sheet

ENC28J60.pdf


■ Ethercard Library (ENC28J60)

ethercard-master.zip


라이브러리는 https://github.com/jcw/ethercard 에서 다운로드 받는다.

라이브러리의 설치는 아두이노 IDE 설치폴더 하위의 libraries 폴더에 위의 ZIP 파일의 압축을 풀어 복사해 넣으면 된다.


연결

라이브러리 홈페이지에서 캡처한 아두이노 우노와 메가의 핀연결 방법



아두이노 우노와의 연결 모습 10개중 6개의 핀을 연결하였다.



DHCP 테스트


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
#include <EtherCard.h>
 
//MAC주소-아래에서 지정한대로 설정됨
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
 
//이더넷 송수신 데이터 메모리 버퍼 설정
byte Ethernet::buffer[700];
 
void setup () {
  Serial.begin(57600);
  Serial.println(F("\n[testDHCP]"));
 
  //MAC 주소 찍기
  Serial.print("MAC: ");
  for (byte i = 0; i < 6++i) {
    Serial.print(mymac[i], HEX);
    if (i < 5)
      Serial.print(':');
  }
  Serial.println();
 
  //이더넷 연결 실패
  if (ether.begin(sizeof Ethernet::buffer, mymac) == 0
    Serial.println(F("Failed to access Ethernet controller"));
  
  //DHCP 설정
  Serial.println(F("Setting up DHCP"));
  if (!ether.dhcpSetup())
    Serial.println(F("DHCP failed"));
  
  ether.printIp("My IP: ", ether.myip);
  ether.printIp("Netmask: ", ether.netmask);
  ether.printIp("GW IP: ", ether.gwip);
  ether.printIp("DNS IP: ", ether.dnsip);
}
 
void loop () {}
cs


공유기에 연결하여 IP, 게이트웨이, DNS, Netmask 등의 값을 설정하고 받아온다.


공유기의 DHCP에 의해 IP가 192.168.0.14 로 할당되었고 다른 네트워크 정보도 자동으로 설정되고 MAC 주소를 변경하지 않는다면 공유기에서 계속 이 IP를 사용하게 된다.



PING 테스트

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
// Ping a remote server, also uses DHCP and DNS.
// 2011-06-12 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
 
#include <EtherCard.h>
 
// ethernet interface mac address, must be unique on the LAN
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
 
byte Ethernet::buffer[700];
static uint32_t timer;
 
// called when a ping comes in (replies to it are automatic)
// PING 을 받으면 보내주는 Call Back
static void gotPinged (byte* ptr) {
  ether.printIp(">>> ping from: ", ptr);
}
 
void setup () {
  Serial.begin(57600);
  Serial.println("\n[pings]");
  
  if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
    Serial.println(F("Failed to access Ethernet controller"));
  if (!ether.dhcpSetup())
    Serial.println(F("DHCP failed"));
 
  ether.printIp("IP:  ", ether.myip);
  ether.printIp("GW:  ", ether.gwip);
 
#if 1
  // use DNS to locate the IP address we want to ping
  if (!ether.dnsLookup(PSTR("www.google.com")))
    Serial.println("DNS failed");
#else
  ether.parseIp(ether.hisip, "74.125.77.99");
#endif
  ether.printIp("SRV: ", ether.hisip);
    
  // call this to report others pinging us
  ether.registerPingCallback(gotPinged);
  
  timer = -9999999// start timing out right away
  Serial.println();
}
 
void loop () {
  word len = ether.packetReceive(); // go receive new packets
  word pos = ether.packetLoop(len); // respond to incoming pings
  
  // report whenever a reply to our outgoing ping comes back
  if (len > 0 && ether.packetLoopIcmpCheckReply(ether.hisip)) {
    Serial.print("  ");
    Serial.print((micros() - timer) * 0.0013);
    Serial.println(" ms");
  }
  
  // ping a remote server once every few seconds
  if (micros() - timer >= 5000000) {
    ether.printIp("Pinging: ", ether.hisip);
    timer = micros();
    ether.clientIcmpRequest(ether.hisip);
  }
}
cs

이 소스는 Google 의 IP를 알아와서 Ping 을 해보고 반대로 아두이노에 Ping이 들어왔을때 응답(Call Back) 해 주는 소스이다.

아두이노에 Ping 호출


Google 의 Ping 과 아두이노로 Ping을 보낸 컴퓨터의 정보 표시


Ping 호출에 대한 응답이 정상적으로 이루어지니 미니 웹서버로의 활용도 가능해 보인다.




미니 웹서버

웹브라우저의 요청에 준비된 HTML을 뿌려주는 기능이다.

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
// Present a "Will be back soon web page", as stand-in webserver.
// 2011-01-30 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
 
#include <EtherCard.h>
 
//0은 DHCP, 1은 Static으로 설정
#define STATIC 0  // set to 1 to disable DHCP (adjust myip/gwip values below)
 
//Static IP 일 경우 설정
#if STATIC
// ethernet interface ip address
static byte myip[] = { 192,168,1,200 };
// gateway ip address
static byte gwip[] = { 192,168,1,1 };
#endif
 
// ethernet mac address - must be unique on your network
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
 
byte Ethernet::buffer[500]; // tcp/ip send and receive buffer
 
//요청이 왔을 경우 뿌려줄 HTML 소스
const char page[] PROGMEM =
"HTTP/1.0 503 Service Unavailable\r\n"
"Content-Type: text/html\r\n"
"Retry-After: 600\r\n"
"\r\n"
"<html>"
  "<head><title>"
    "아두이노 웹서버 입니다."
  "</title></head>"
  "<body>"
    "<h3>아두이노 웹서버</h3>"
    "<p><em>"
      "아두이노 웹서버 입니다.<br />"
      "공사중 입니다."
    "</em></p>"
  "</body>"
"</html>"
;
 
void setup(){
  Serial.begin(57600);
  Serial.println("\n[backSoon]");
  
  if (ether.begin(sizeof Ethernet::buffer, mymac) == 0
    Serial.println( "Failed to access Ethernet controller");
#if STATIC
  ether.staticSetup(myip, gwip);
#else
  if (!ether.dhcpSetup())
    Serial.println("DHCP failed");
#endif
 
  ether.printIp("IP:  ", ether.myip);
  ether.printIp("GW:  ", ether.gwip);  
  ether.printIp("DNS: ", ether.dnsip);  
}
 
void loop(){
  //웹브라우저의 요청이 들어왔을 경우 page 의 내용을 보내줌
  // wait for an incoming TCP packet, but ignore its contents
  if (ether.packetLoop(ether.packetReceive())) {
    memcpy_P(ether.tcpOffset(), page, sizeof page);
    ether.httpServerReply(sizeof page - 1);
  }
}
cs

브라우저에 아두이노의 IP를 입력하면 ENC28J60은 TCP 패킷을 받게 된다. 이 요청에 아두이노에 저장된 HTML 소스(page 변수) 를 웹브라우저로 보내주는 소스이다. 공유기 환경의 경우 포트포워딩을 이용하면 외부 인터넷에서도 접근이 가능하게 만들 수 있다.


웹브라우저에서 아두이노에 할당된 IP를 입력하면 위와 같이 나오게 된다.



트위터에 글 보내기

트위터에 글을 보내기 위해서는 우선 트위터 계정이 있어야 하며 트위터에 아두이노가 로그인하기 위한 인증키(보안토큰)를 발급 받아야 한다.


http://arduino-tweet.appspot.com/oauth/twitter/login 사이트에 들어가서 트위터 아이디와 패스워드를 입력하고 Authorize App 을 클릭한다.


잠시 후 인증이 완료되고 보안토큰의 값이 나온다. 이를 복사하여 아래의 소스 17행에 붙여 넣기 한다.


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
// Twitter client sketch for ENC28J60 based Ethernet Shield. Uses 
// arduino-tweet.appspot.com as a OAuth gateway.
// Step by step instructions:
// 
//  1. Get a oauth token:
//     http://arduino-tweet.appspot.com/oauth/twitter/login
//  2. Put the token value in the TOKEN define below
//  3. Run the sketch!
//
//  WARNING: Don't send more than 1 tweet per minute! (1분에 1개 이상의 트위터를 보내지 마시오)
//  NOTE: Twitter rejects tweets with identical content as dupes (returns 403)
 
#include <EtherCard.h>
 
// 트위터 API 이용을 위해 OAUTH 토큰값을 넣는다. (트위터 ID/패스워드 필요)
// http://arduino-tweet.appspot.com/oauth/twitter/login 여기에서 트위터 로그인하고 토큰발급 받는다.
#define TOKEN   "발급받은 토큰값을 여기에 입력"
 
// ethernet interface mac address, must be unique on the LAN
byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x63 };
 
const char website[] PROGMEM = "arduino-tweet.appspot.com";
 
static byte session;
 
byte Ethernet::buffer[700];
Stash stash;
 
static void sendToTwitter () {
  Serial.println("Sending tweet...");
  byte sd = stash.create();
 
  const char tweet[] = "hello world!!!!!!"//트위터에 올릴 글 내용
  stash.print("token=");
  stash.print(TOKEN);
  stash.print("&status=");
  stash.println(tweet);
  stash.save();
  int stash_size = stash.size();
 
  // Compose the http POST request, taking the headers below and appending
  // previously created stash in the sd holder.
  Stash::prepare(PSTR("POST http://$F/update HTTP/1.0" "\r\n"
    "Host: $F" "\r\n"
    "Content-Length: $D" "\r\n"
    "\r\n"
    "$H"),
  website, website, stash_size, sd);
 
  // send the packet - this also releases all stash buffers once done
  // Save the session ID so we can watch for it in the main loop.
  session = ether.tcpSend();
}
 
void setup () {
  Serial.begin(57600);
  Serial.println("\n[Twitter Client]");
 
  if (ether.begin(sizeof Ethernet::buffer, mymac) == 0
    Serial.println(F("Failed to access Ethernet controller"));
  if (!ether.dhcpSetup())
    Serial.println(F("DHCP failed"));
 
  ether.printIp("IP:  ", ether.myip);
  ether.printIp("GW:  ", ether.gwip);  
  ether.printIp("DNS: ", ether.dnsip);  
 
  if (!ether.dnsLookup(website))
    Serial.println(F("DNS failed"));
 
  ether.printIp("SRV: ", ether.hisip);
 
  sendToTwitter();
}
 
void loop () {
 
  //트위터 서버로 부터 받은 결과를 출력
  ether.packetLoop(ether.packetReceive());
  
  const char* reply = ether.tcpReply(session);
  if (reply != 0) {
    Serial.println("Got a response!");
    Serial.println(reply);
  }
 
}
 
cs


소스를 업로드 하고 트위터를 들어가서 보면 다음과 같이 소스에서 설정한 글이 올라가 있다.


트위터 보내기에서 중요한 점은 적어도 글을 올리고 1분 뒤에 글을 올려야 한다는 것이다. 그리고 같은 글을 계속 보내도 차단 될 수 있다고 한다. 만약 에러가 나는 사람은 1분 이상 기다렸다가 다시 시도해 보거나 트위터 내용을 다른 글로 수정하고 보내면 될 것이다.



웹서버로 데이터 보내기 (GET 방식)

아두이노가 웹클라이언트로 웹서버에 데이터를 보낼 수도 있다. 이를 위해서는 데이터를 받을 별도의 웹서버가 필요하며 받을 데이터를 처리할 웹프로그래밍이 필요하다. 예를 들면 아두이노에서 온도 데이터를 웹서버로 보냈다면 이를 받아서 DB에 저장하는 등의 처리과정이 필요한 것이다. 아무튼 웹서버에서 데이터를 받아서 어떻게 처리하는지는 각자의 목적이 다를 것이므로 여기서는 ENC28J60 과 아두이노를 이용해서 GET 방식으로 웹서버에 데이터를 보내는 방법을 알아보겠다.


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
 
// Demo using DHCP and DNS to perform a web client request.
// 2011-06-08 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
 
#include <EtherCard.h>
 
// ethernet interface mac address, must be unique on the LAN
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
 
byte Ethernet::buffer[700];
static uint32_t timer;
 
//GET 방식으로 데이터를 보낼 주소
const char website[] PROGMEM = "192.168.0.7";
 
// called when the client request is complete
static void my_callback (byte status, word off, word len) {
  Serial.println(">>>");
  Ethernet::buffer[off+300= 0;
  Serial.print((const char*) Ethernet::buffer + off);
  Serial.println("...");
}
 
void setup () {
  Serial.begin(57600);
  Serial.println("\n[webClient]");
 
  if (ether.begin(sizeof Ethernet::buffer, mymac) == 0
    Serial.println( "Failed to access Ethernet controller");
  if (!ether.dhcpSetup())
    Serial.println("DHCP failed");
 
  ether.printIp("IP:  ", ether.myip);
  ether.printIp("GW:  ", ether.gwip);  
  ether.printIp("DNS: ", ether.dnsip);  
 
  if (!ether.dnsLookup(website))
    Serial.println("DNS failed");
    
  ether.printIp("SRV: ", ether.hisip);
}
 
void loop () {
  ether.packetLoop(ether.packetReceive());
  
  if (millis() > timer) {
    timer = millis() + 5000;    //5초마다 보냄
    Serial.println();
    Serial.print("<<< REQ ");
    
    //웹서버 포트 입력
    ether.hisport = 80;
 
    //GET 방식으로 데이터를 받을 URL 과 보낼 데이터
    ether.browseUrl(PSTR("/receive/receive.php?"), "get=Hello", website, my_callback);
  }
}
cs


위의 소스는 'http://192.168.0.7/receive/receive.php' 주소에 get의 ID 값으로 Hello 라는 데이터를 보내는 예제이다. 8행 MAC 주소, 14행 사이트 주소, 52행 포트번호, 55행 데이터 받을 웹주소 및 보낼 데이터 등은 자신의 상황에 맞게 수정이 필요하다.


ENC28J60을 잘 활용한다면 원격지에서 특정지역의 날씨데이터를 측정하여 웹서버로 보내 데이터베이스를 구축한다거나 요즘 뜨고 있는 IoT 기술 등에도 쓰일 수 있을 것 같다.

반응형
Posted by 대네브 (deneb)

댓글을 달아 주세요

  1. 소프트웨어공학 전공

    아 감사합니다.... 정말 감사해요.
    알리익스프레스에서 직구했는데 인터넷 검색해보고 왜 안되나 고생했습니다.
    정말 감사합니다.^^

    2016.05.11 00:28 [ ADDR : EDIT/ DEL : REPLY ]
  2. 딜케이리

    아두이노 메가를 사용해서 해보는데 static void sendToTwitter () 이 부분에서 코드 에러가 납니다 왜그런지 알려주실수 있나요????

    2016.05.15 19:22 [ ADDR : EDIT/ DEL : REPLY ]
    • 위에 핀연결은 우노와 틀리게 하신거 맞죠? 제가 메가는 한번도 다뤄본적이 없어요. 가지고 있지도 않구요... 들은바로는 메가는 우노의 라이브러리와 코딩이 다른 경우도 있다고 들었습니다. 이런 부분들 점검해 보시는게...

      2016.05.15 21:00 신고 [ ADDR : EDIT/ DEL ]
  3. 궁금이

    같은 이더넷보드를 썼는데 왜 트위터에 보내는 코드와 DHCP에서의 MAC주소가 다른가요?????

    2016.05.15 21:33 [ ADDR : EDIT/ DEL : REPLY ]
    • 소스에 맥주소는 별 의미가 없더라구요. 그냥 아무렇게 설정하는대로 들어갑니다.

      2016.05.15 21:37 신고 [ ADDR : EDIT/ DEL ]
  4. 소프트웨어초보

    트위터에 전송하는거 한번만 되는데 지속적으로 일정 시간마다 보내는 방법은 무엇인가요?

    2016.06.10 20:15 [ ADDR : EDIT/ DEL : REPLY ]
    • 위의 소스는 setup에 트위터 보내는 함수가 있어서 한번만 보내는 소스 입니다. 여러번 보내시려면 loop 안에 트위터 보내는 함수sendToTwitter() 를 Call 하는걸로 작성해야 합니다. 위의 소스를 참고해서 좀 수정하면 될겁니다. 단 1분 이상의 텀을 두고 보내야 제대로 올라 갑니다.

      2016.06.10 22:00 신고 [ ADDR : EDIT/ DEL ]
  5. 중2병

    아이피를 설정하려는데(참고로 제 아이피는 172.30.1.xx) 172.30.1.xx에서 xx만 바꾸고 그걸로 아이피를 써도 되나요??

    2016.08.16 08:18 [ ADDR : EDIT/ DEL : REPLY ]
    • dhcp 라면 자동으로 공유기에서 할당해 줄 것이고 아니라면 말하신 것처럼 대부분 뒷 자리가 변경이 되죠

      2016.08.16 10:26 신고 [ ADDR : EDIT/ DEL ]
  6. 랜 연결이 안돼요ㅠㅠ
    뭐가 문제일까요?

    2016.08.17 14:56 [ ADDR : EDIT/ DEL : REPLY ]
    • 이 질문 만으로는 뭐가 잘 못 됐는지 파악이 안되네요. 다시 모든걸 확인해 보시길

      2016.08.17 17:42 신고 [ ADDR : EDIT/ DEL ]
    • 그냥 뭐랄까..공유기가 거부하는듯한 느낌이네요..
      (참고:kt homehub를 씁니다)

      2016.08.17 22:34 [ ADDR : EDIT/ DEL ]
  7. 질문이

    mac주소는 마음대로 써도되나요?

    2016.08.17 15:32 [ ADDR : EDIT/ DEL : REPLY ]
  8. 이더넷

    저는 w5100 이더넷쉴드를 우노에 붙여서 사용중인데 위에 쓰신함수가 제 이더넷쉴드에 적용이 되는지 궁금합니다...아니라면 그 함수가 뭔지 알고싶습니다. 아두이노 자체에서 핑을 보내고 받아서 패킷분석까지 하고싶은데 함수를 잘모르겟습니다..만들어써야되는지....

    2017.03.22 23:33 [ ADDR : EDIT/ DEL : REPLY ]
    • https://github.com/CisecoPlc/Arduino-W5100-W5200 여기에 w5100 관련 라이브러리와 예제들이 많이 있네요.

      2017.03.23 00:12 신고 [ ADDR : EDIT/ DEL ]
  9. 카르마

    안녕하세요. 질문 하나 드려도 될까요?
    아두이노 웹서버를 짜고 랜선을 연결할 때 통신사 가입한 집에서 사용할 경우(와이파이에 연결하는 게 아닌 바로 랜선 열결)
    스마트폰 브라우저에서 3G나 LTE 등으로 접근이 가능한가요? 와이파이의 경우엔 포트포워드를 해야한단 말을 들어서..

    2017.05.19 20:47 [ ADDR : EDIT/ DEL : REPLY ]
    • 가능합니다. 질문에 답이 있네요.
      포트포워드와 더 편리하게 쓰려면 ddns 를 쓰면 됩니다.

      2017.05.19 21:03 신고 [ ADDR : EDIT/ DEL ]
  10. 카르마

    엇 그럼 반드시 공유기를 사용해야 하고...
    그냥 랜선 꼽는걸론 포트포워드나 ddns를 못 쓰는 건가요?

    2017.05.19 22:36 [ ADDR : EDIT/ DEL : REPLY ]
    • 가정용 인터넷이면 공인 유동ip겠죠? 거기에 공유기를 달면 사설 ip가 되구요. 공유기를 안쓰고 통신사 모뎀에 직접 연결한다면 이 공인 유동 ip를 알아야 합니다. 컴에서 ipconfig로 알 수 있을 겁니다.

      그리고 ddns 서비스는 유동ip 를 도메인에 연결해주어 ip변경이 생기더라도 언제 어디서든 서버에 연결해주는 역할을 합니다. 이 서비스는 일부 공유기 회사의 제품에서만 지원을 합니다. 예를들어 iptime공유기의 iptime.org 나 디링크 공유기 등이 지원합니다. 공유기 구매시 ddns 지원여부를 확인해서 구입하면 됩니다.

      2017.05.20 13:43 신고 [ ADDR : EDIT/ DEL ]
    • 포트포워드도 당연 공유기에서만 지원을 하는 기능 입니다

      2017.05.20 13:44 신고 [ ADDR : EDIT/ DEL ]
    • 카르마

      아하! 그렇군요.
      마침 ddns를 지원하는 것 같으니 먼저 이 방법으로 시도해 보고 다음에 다른 방법들도 해보면 되겠군요.
      감사합니다!

      2017.05.20 14:24 [ ADDR : EDIT/ DEL ]
  11. 안녕하세요

    serial.begin을 항상 57600으로 설정해야 하나요??

    2017.05.24 09:46 [ ADDR : EDIT/ DEL : REPLY ]
    • 시리얼모니터의 baud rate만 같은 속도로 맞추면 다르게해도 상관 없습니다.

      2017.05.24 09:54 신고 [ ADDR : EDIT/ DEL ]