아두이노 보드를 이용해서 네트워크 통신을 가능하게 만드는 방법을 설명한다.

Ethernet

이더넷을 사용하기 위해서는 이더넷 포트가 달려있는 이더넷 쉴드를 사용해야 한다.
아두이노 보드와 핀 매핑이 동일하기 때문에 꼽기만 하면 된다. 랜선(RJ-45)을 꼽기만 하면 된다.

이더넷 및 SD메모리 쉴드는 아두이노 보드와 SPI통신으로 연결해서 사용된다. 그리고 SPI통신을 위해 D11, D12, D13 을 사용한다. 그리고 D10번핀은 이더넷 선택에 사용되고, D4번핀은 SD 선택에 사용된다.
그리고 이더넷과 SD장치는 SPI통신라인 공유로인해 동시에 접근하면 안된다. 그래서 장치 선택 핀을 통해 번갈아 장치을 선택해가며 사용해야 한다. 장치 미사용 설정시에는, 장치선택 핀을 출력모드로 설정한 후 그 핀에 High 값을 출력 하면 된다.

네트워크를 접속하기 위해서는 아두이노 보드 만큼이나 외부 네트워크 환경 설정 또한 중요하다.

참고로 나의 네트워크 환경은 아래와 같다.

'외부 네트워크 ←(WiFi)→ 노트북 ←(USB to Ethernet)→ 아두이노'

노트북에서 2개의 네트워크 인터페이스(wlan0, eth0)를 사용하고 있고, 아두이노와 연결된 eth0 로 부터 받은 데이터 패킷을 외부 네트워크와 연결된 wlan0 로 보내야 한다.
따라서 다음과 같은 명령을 실행해야 한다.

#ifconfig eth0 1.1.1.1 up
#iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE 
#echo 1 > /proc/sys/net/ipv4/ip_forward 

구글 웹서버에 접속해서 데이터를 받아오는 코드이다. DNS 를 사용하지 않기 때문에 구글 웹서버의 IP 주소를 바로 사용했다.

#include <SPI.h>         // 라이브러리 추가
#include <Ethernet.h>         // 라이브러리 추가
 
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};     // 아두이노 MAC 주소 지정
byte ip[] = { 1, 1, 1, 2};                           // IP 주소 할당
byte server[] = {74, 125, 235, 71};             // 접속할 google.com IP 주소
 
EthernetClient client;
 
void setup()
{
  Serial.begin(9600);
  Ethernet.begin(mac, ip);         // google.com 접속 준비
  delay(1000);                     // 이더넷 하드웨어를 초기화 하는 동안 잠깐 기다린다
 
  Serial.println("connecting");
 
  if(client.connect(server, 80))              // google.com 서버에 80번 포트로 접속
  {
    Serial.println("connected");
    client.println("GET /search?q=arduino HTTP/1.0");    // google.com 서버에 arduino 검색하도록 쿼리를 보냄
    client.println();
  }
  else
  {
    Serial.println("connection failed");
  }
}
 
void loop()
{
  if(client.available())             // 연결이 된 상태라면
  {
    char c = client.read();           // 서버로부터의 메세지를 읽어들인다. 수신된 모든 데이터를 시리얼 모니터에 표시함
    Serial.print(c);
  }
 
  if(!client.connected())          //  연결이 끊어진 상태라면
  {
    Serial.println();
    Serial.println("disconnecting");
    client.stop();                               // 접속을 끊는다
    for(;;)
    ;
  }
}

터미널 창을 열어 구글 웹서버로 부터 HTML 코드를 받아온다면, 성공이다.

이제는 DHCP 서버로부터 받은 IP 및 네트워크 정보를 가지고 구글에 접속해보자.
그전에 사전 작업이 필요하다. 노트북을 DHCP 서버로 만들어야 한다.

#ifconfig eth0 1.1.1.1 up             // DHCP 서버의 IP 주소 설정
#apt-get install dhcpd

DHCP 클라이언트에게 줄 정보들을 설정해야 한다. /etc/udhcpd.conf 파일을 수정한다.

start     1.1.1.2
end      1.1.1.254
 
interface    eth0
 
opt     dns     210.220.163.82 219.250.36.130
option  subnet  255.255.255.0
opt     router  1.1.1.1
opt     wins    192.168.10.10
option  domain  local
option  lease   864000          # 10 

dhcpd 데몬을 활성화 시켜야 한다. /etc/default/udhcpd 파일을 수정한다.

# Comment the following line to enable
DHCPD_ENABLED="yes"               // YES 로 수정
 
# Options to pass to busybox' udhcpd.
#
# -S    Log to syslog
# -f    run in foreground
 
DHCPD_OPTS="-S -f"            // 실시간으로 로그를 보고 싶다면, -f 옵션을 추가
#include <SPI.h>
 
#include <EthernetUdp.h>
#include <Dhcp.h>
#include <Dns.h>
#include <Ethernet.h>
#include <EthernetServer.h>
#include <EthernetClient.h>
#include <util.h>
 
 
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
byte server[] = {74, 125, 235, 71};
 
EthernetClient client;
 
void setup()
{
  Serial.begin(9600);
  if(Ethernet.begin(mac) == 0)   // MAC 주소와 DHCP 를 사용하여 이더넷을 시작한다.
  {
    Serial.println("Failed to configure using DHCP");
    while(true)        // 실행 중인 지점이 없기 때문에 무한 루프에 빠진다
    ;
  }
 
  delay(1000);
 
  Serial.print("This IP address: ");
  IPAddress myIPAddress = Ethernet.localIP();     // DHCP 서버로부터 받은 IP 주소값을 저장
  Serial.print(myIPAddress);
 
  if(client.connect(server, 80) > 0)
  {
    Serial.println("connected");
    client.println("GET /search?q=arduino HTTP/1.0");    // google.com 서버에 arduino 검색하도록 쿼리를 보냄
    client.println();
  }
  else
  {
    Serial.println("connection failed");
  }
}
 
void loop()
{
  if(client.available())             // 연결이 된 상태라면
  {
    char c = client.read();           // 서버로부터의 메세지를 읽어들인다. 수신된 모든 데이터를 시리얼 모니터에 표시함
    Serial.print(c);
  }
 
  if(!client.connected())          //  연결이 끊어진 상태라면
  {
    Serial.println();
    Serial.println("disconnecting");
    client.stop();                               // 접속을 끊는다
    for(;;)
    ;
  }
}

DHCP 서버로 부터 정상적으로 정보를 받고 이를 토대로 구글에 접속했는지 확인해보자.
앞선 예제코드와 다른 점은 IP(또는 게이트웨이) 주소 변수가 없다는 것이다. 대신 여기서는 스케치가 시작될 때 DHCP 서버로부터 해당 값을 받아온다.
그리고 ethernet.begin 명령문의 성공여부도 확인한다. 이렇게 하는 이유는 DHCP 서버에서 제공한 IP 주소가 유효한지 확인하기 위한 것이다. IP 주소가 유효하지 않으면 인터넷에 엑세스할 수 없다.

앞선 코드에서는 접속하려는 서버의 IP 주소를 사용했다. 하지만, DNS 를 이용한다면, 도메인 주소로도 접속이 가능하다.
물론 DHCP 서버로 부터 정확한 DNS 서버 주소를 받아와야 한다는 전제 조건이 있지만 말이다.

#include <SPI.h>
 
#include <EthernetUdp.h>
#include <Dhcp.h>
#include <Dns.h>
#include <Ethernet.h>
#include <EthernetServer.h>
#include <EthernetClient.h>
#include <util.h>
 
 
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
 
char serverName[] = "www.google.com";            // 도메인 주소 할당
 
EthernetClient client;
 
void setup()
{
  Serial.begin(9600);
  if(Ethernet.begin(mac) == 0)
  {
    Serial.println("Failed to configure using DHCP");
    while(true)
    ;
  }
 
  delay(1000);
 
  int ret = client.connect(serverName, 80);
 
  if(ret == 1)
  {
    Serial.println("connected");
    client.println("GET /search?q=arduino HTTP/1.0");    // google.com 서버에 arduino 검색하도록 쿼리를 보냄
    client.println();
  }
  else
  {
    Serial.println("connection failed");
    Serial.print(ret, DEC);
  }
}
 
void loop()
{
  if(client.available())             // 연결이 된 상태라면
  {
    char c = client.read();           // 서버로부터의 메세지를 읽어들인다. 수신된 모든 데이터를 시리얼 모니터에 표시함
    Serial.print(c);
  }
 
  if(!client.connected())          //  연결이 끊어진 상태라면
  {
    Serial.println();
    Serial.println("disconnecting");
    client.stop();                               // 접속을 끊는다
    for(;;)
    ;
  }
}

client.connect 함수는 DNS 서버에 의해 호스트 이름이 IP 주소로 변환되어 클라이언트가 성공적으로 연결할 수 있으면 1을 리턴한다.
다음은 client.connect 의 리턴값에 대한 설명이다.

반환값 설명
1 성공
0 연결 실패
-1 지정한 DNS 서버가 없음
-2 DNS 레코드가 없음
-3 제한 시간 초과

오류가 -1인 경우에는 DNS 서버를 수동으로 구성해서 사용해야 한다. DNS 서버주소는 보통 DHCP 서버를 통해 자동으로 제공받게 되지만 쉴드를 수동으로 구성할 경우에는 DNS 서버주소를 사용자가 직접 제공해야 한다. 그렇지 않으면 connect 함수가 -1 을 리턴한다.

서버로부터 데이터를 요청하고 이를 받아 가공해보자.

구글 접속 후 '1000 km in miles' 로 검색해서 나오는 결과 값 중 '1000 km = 621.371192 miles' 문자열을 파싱해서 출력한다.

#include <SPI.h>
 
#include <EthernetUdp.h>
#include <Dhcp.h>
#include <Dns.h>
#include <Ethernet.h>
#include <EthernetServer.h>
#include <EthernetClient.h>
#include <util.h>
 
 
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
 
char serverName[] = "www.google.com";            // 도메인 주소 할당 
 
EthernetClient client;
 
int result;
 
void setup()
{
  Serial.begin(9600);
  if(Ethernet.begin(mac) == 0)
  {
    Serial.println("Failed to configure using DHCP");
    while(true)
    ;
  }
 
  delay(1000);
 
  Serial.println("connecting");
}
 
void loop()
{
  if(client.connect(serverName, 80) > 0)
  {
    Serial.print("connected..");
    client.println("GET /search?q=1000+km+in+miles HTTP/1.0");
    client.println();
  }
  else
  {
    Serial.println("connection failed");
  }
 
  if(client.connected())
  {
    if(client.find("<b>1000 km"))
    {
      if(client.find("= "))
      {
        result = client.parseInt();
        Serial.print("50 km is ");
        Serial.print(result);
        Serial.print(" miles");
      }
    }
    else
    Serial.println("result not found");
    client.stop();
    delay(10000);
  }
  else
  {
    Serial.println();
    Serial.println("not connected");
    client.stop();
    delay(1000);
  }
}

아두이노에 간단한 웹서버를 올려서, 간단한 정보를 볼 수있게 할 수 있다. 현재 지원되는 언어는 HTML 뿐이다. '파일 → 예제 → Ethernet → Webserver' 를 선택한다.
5초에 한번씩 reload 되면서, A0 ~ A5 핀의 전압을 읽어 출력한다.

/*
  Web Server
 
 A simple web server that shows the value of the analog input pins.
 using an Arduino Wiznet Ethernet shield. 
 
 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13
 * Analog inputs attached to pins A0 through A5 (optional)
 
 created 18 Dec 2009
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe
 
 */
 
#include <SPI.h>
#include <Ethernet.h>
 
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(1,1,1,1);                                // 서버 주소 IP
 
// Initialize the Ethernet server library
// with the IP address and port you want to use 
// (port 80 is default for HTTP):
EthernetServer server(80);
 
void setup() {
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
 
 
  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
}
 
void loop() {
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connnection: close");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
                    // add a meta refresh tag, so the browser pulls again every 5 seconds:
          client.println("<meta http-equiv=\"refresh\" content=\"5\">");
          // output the value of each analog input pin
          for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
            int sensorReading = analogRead(analogChannel);
            client.print("analog input ");
            client.print(analogChannel);
            client.print(" is ");
            client.print(sensorReading);
            client.println("<br />");       
          }
          client.println("</html>");
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        } 
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disonnected");
  }
}

인터넷 타임서버로부터 시간값을 받아오면, 오차없는 시간을 유지할 수 있다.

#include <SPI.h>
 
#include <EthernetServer.h>
#include <Dns.h>
#include <EthernetClient.h>
#include <EthernetUdp.h>
#include <Ethernet.h>
#include <Dhcp.h>
#include <util.h>
 
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
 
unsigned int localPort = 8888;
 
IPAddress timeServer(192,43,244,18);
const int NTP_PACKET_SIZE = 48;
 
byte packetBuffer[NTP_PACKET_SIZE];
 
EthernetUDP Udp;
 
void setup()
{
  Serial.begin(9600);
 
  if(Ethernet.begin(mac) == 0)
  {
    Serial.println("Failed to configure Ethernet using DHCP");
 
    for(;;)
    ;
  }
  Udp.begin(localPort);
}
 
void loop()
{
  sendNTPpacket(timeServer);
  delay(1000);
 
  if(Udp.parsePacket())
  {
    Udp.read(packetBuffer, NTP_PACKET_SIZE);
 
    unsigned long hi = word(packetBuffer[40], packetBuffer[41]);
    unsigned long low = word(packetBuffer[42], packetBuffer[43]);
    unsigned long secsSince1900 = hi << 16 | low;
 
    Serial.print("Seconds since Jan 1 1900 = ");
    Serial.println(secsSince1900);
 
    Serial.print("Unix time = ");
    const unsigned long seventyYears = 2208988800UL;
    unsigned long epoch = secsSince1900 - seventyYears;
    Serial.println(epoch);
 
 
    Serial.print("THE UTC time is ");
    Serial.print((epoch % 86400L) / 3600);
    Serial.print(':');
 
    if(((epoch % 3600) / 60) < 10)
    {
      Serial.print('0');
    }
 
    Serial.print((epoch % 3600) / 60);
    Serial.print(':');
 
    if((epoch % 60) < 10)
    {
      Serial.print('0');
    }
    Serial.println(epoch %60);
 
  }
  delay(10000);
}
 
unsigned long sendNTPpacket(IPAddress& address)
{
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
 
  packetBuffer[0] = B11100011;
  packetBuffer[1] = 0;
  packetBuffer[2] = 6;
  packetBuffer[3] = 0xEC;
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49; 
  packetBuffer[15] = 52;
 
  Udp.beginPacket(address, 123);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}
#include <SPI.h>
 
#include <EthernetServer.h>
#include <Dns.h>
#include <EthernetClient.h>
#include <EthernetUdp.h>
#include <Ethernet.h>
#include <Dhcp.h>
#include <util.h>
 
 
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
byte server[] = {184, 106, 153, 149};
 
char *thingtweetAPIKey = "LJ8BNZW5ANF7N3RG";
 
EthernetClient client;
 
boolean MsgSent = false;
const int Sensor = 2;
 
void setup()
{
  Serial.begin(9600);
  if(Ethernet.begin(mac) == 0)
  {
    Serial.println("Failed dhcp");
    while(true)
    ;
  }
  pinMode(Sensor, INPUT);
  digitalWrite(Sensor, HIGH);
  delay(1000);
  Serial.println("Ready");
}
 
void loop()
{
  if(digitalRead(Sensor) == HIGH)
  {
    if(MsgSent == false)
    {
      MsgSent = sendMessage("Mail has been delivered");
      if(MsgSent)
        Serial.println("tweeted successfully");
      else
        Serial.println("Unable tweet");
    }
  }
  else
  {
    MsgSent = false;
  }
  delay(100);
}
 
boolean sendMessage(char *message)
{
  boolean result = false;
 
  const int tagLen = 16;
  int msgLen = strlen(message) + tagLen + strlen(thingtweetAPIKey);
  Serial.println("connecting ...");
 
  if(client.connect(server, 80))
  {
    Serial.println("making POST request...");
 
    client.print("POST /apps/thingtweet/1/statuses/update HTTP/1.1\r\n");
    client.print("Host: api.thingspeak.com\r\n");
    client.print("Connection: close\r\n");
    client.print("Content-Type: application/x-www-form-urlencoded\r\n");
    client.print("Content-Length: ");
    client.print(msgLen);
    client.print("\r\n\r\n");
    client.print("api_key=");
    client.print(thingtweetAPIKey);
    client.print("&status=");
    client.print(message);
    client.println("\r\n");
  }
  else
  {
    Serial.println("Connection failed");
  }
 
  if(client.connected())
  {
    Serial.println("Connected");
    if(client.find("HTTP/1.1") && client.find("200 OK"))
    {
      result = true;
    }
    else
      Serial.println("Dropping connection - no 200 ok");
  }
  else
  {
    Serial.println("Disconnected");
  }
  client.stop();
  client.flush();
 
  return result;
}
  • computer/embedded/아두이노_그대로_따라하기_-_9.네트워크_통신.txt
  • Last modified: 3 years ago
  • by likewind