Ethernet Shield
UNODUE

概要

Ethernet Shieldを利用したTCP/IP通信の実験です。標準で付属のEthernetライブラリを利用します。スケッチはArduinoソフトウェアについてくるDhcpAddressPrinter、WebClient、WebServerをベースに少し修正しています。

Arduino Uno と Arduino Due の両方で動作することを確認しました。Arduino Dueの場合も、ICSPピンが正しく刺さるように(デジタルピンやアナログピンの番号が同じとなるように)Ethernet Shieldを取り付ければ動作するようです。

目的

Ethernetライブラリの使い方と動作を少しずつ見ていきます。最終的にはTCPの簡単なクライアントプログラムとサーバプログラムを作成します。

  1. 共通編
  • IPアドレスを設定する。
    • DHCPサーバからIPアドレスを取得する。
    • IPアドレスを指定する。
  1. クライアント編
    • 簡単なWebクライアントを作成する。
  2. サーバ編
    • 簡単なWebサーバを作成する。

実験

共通編

クライアント・サーバのどちらを利用するにもIPアドレスを設定する必要があります。ここではIPアドレスの設定方法を確認します。

IPアドレスを設定する

EthernetClassクラスのEthernetというオブジェクト(変数)に、MACアドレスやIPアドレスなどを保持します。EthernetというオブジェクトはEthernetClassライブラリの中で事前に定義されています。シリアル通信に利用するSerialというオブジェクトと同様と考えることができます。

IPアドレスは自分で指定することも、DHCPサーバから取得することもできます。

DHCPサーバからIPアドレスを取得する。

Ethernet.begin()という関数を利用してIPアドレスを設定します。この関数はパラメータによって動作が異なります。DHCPサーバからIPアドレスを取得するときは、この関数にMACアドレスだけを渡します。IPアドレスの取得に成功すると1が返り、失敗すると0が返ります。

MACアドレスはEthernet Shieldの裏に記載されています。Ethernet ShieldをArduino本体に装着する前にMACアドレスをメモしておかないと、後で苦労するかもしれません。隙間から見えなくもありませんが。

 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
#include <SPI.h>
#include <Ethernet.h>
 
// MACアドレスの定義
byte mac_address[] = {
  0x90, 0xA2, 0xDa, 0x0D, 0xD2, 0xEF
};
 
void setup() {
 // put your setup code here, to run once:
  Serial.begin(9600);
 
  // DHCPサーバを用いてIPアドレス、サブネットマスク、ゲートウェイサーバ、DNSサーバを設定する。
  if (Ethernet.begin(mac_address) > 0) {
    Serial.print("IP Address:         ");
    Serial.println(Ethernet.localIP());
    Serial.print("Subnet Mask:        ");
    Serial.println(Ethernet.subnetMask());
    Serial.print("Gateway IP Address: "); 
    Serial.println(Ethernet.gatewayIP());
    Serial.print("DNS Server Address: "); 
    Serial.println(Ethernet.dnsServerIP());
  }
}
 
void loop() {
  // put your main code here, to run repeatedly: 
}

MACアドレスはbyte型の配列で6バイト分を設定します。Ethernet Shieldに割り当てられているアドレスを設定してください。

Ethernet.localIP()は設定されているIPアドレスを取得する関数、Ethernet.subnetMask()は設定されているサブネットマスクを取得する関数、Ethernet.gatewayIP()は設定されているゲートウェイのIPアドレスを取得する関数、Ethernet.dnsServerIP()は設定されているDNSサーバのIPアドレスを取得する関数です。すべてIPAddressクラスを返却します。

IPアドレスクラスはSerial.print()に直接渡すことができます。ドット付きの10進表記で出力されます。

DHCPサーバからIPアドレスを取得した場合は定期的にアドレスの再取得(再リース)を行う必要があります。これは、Ethernet.maintain()という関数を使います。

IPアドレスを指定する

IPアドレスを自分で設定するときもEthernet.begin()を使います。MACアドレス、IPアドレス、DNSサーバアドレス、ゲートウェイアドレス、サブネットマスクを設定することができます。DNSサーバアドレス、ゲートウェイアドレス、サブネットマスクは省略することもできます。省略した場合は以下の値が設定されます。

項目 デフォルト値
DNSサーバアドレス IPアドレスの第4オクテットを1にしたIPアドレス。
ゲートウェイアドレス IPアドレスの第4オクテットを1にしたIPアドレス。
サブネットマスク 255.255.255.0

表の下から順に同時に省略することができます。例えば、サブネットマスクとゲートウェイアドレスを同時に省略することはできますが、DNSサーバを省略してゲートウェイアドレスを指定するということはできません。詳しくはEthernet.begin()のリファレンスを参照してください。

 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
#include <SPI.h>
#include <Ethernet.h>
 
// MACアドレスの定義
byte mac_address[] = {
  0x90, 0xA2, 0xDa, 0x0D, 0xD2, 0xEF
};
 
// IPアドレスの定義
byte IP_address[] = {
  192, 168, 11, 200
};
 
// DNSサーバアドレスの定義
byte dns_address[] = {
  192, 168, 11, 1
};
 
// ゲートウェイアドレスの定義
byte gateway_address[] = {
  192, 168, 11, 1
};
 
// サブネットマスクの定義
byte subnet[] = {
  255, 255, 255, 0
};
 
void setup() {
 // put your setup code here, to run once:
  Serial.begin(9600);
 
  // MACアドレス、IPアドレス、DNSサーバ、
  // ゲートウェイサーバ、サブネットマスクを設定する。
  Ethernet.begin(mac_address, IP_address, dns_address, gateway_address, subnet);
  Serial.print("IP Address:         ");
  Serial.println(Ethernet.localIP());
  Serial.print("Subnet Mask:        ");
  Serial.println(Ethernet.subnetMask());
  Serial.print("Gateway IP Address: "); 
  Serial.println(Ethernet.gatewayIP());
  Serial.print("DNS Server Address: "); 
  Serial.println(Ethernet.dnsServerIP());
}
 
void loop() {
  // put your main code here, to run repeatedly: 
}

IPアドレスやサブネットマスクは、byte型の配列で4バイト分を設定します。各自の環境に合わせて設定してください。IPAddressクラスの変数を使うこともできます。この場合は、例えば、以下のように設定します。

1
IPAddress IP_address(192, 168, 11, 200);

クライアント編

簡単なWebクライアントを作成する

TCPのクライアントは以下の手順で実現することができます。

  1. IPアドレス等設定
  2. クライアント用オブジェクト作成
  3. サーバに接続
  4. データの送受信

上記で、EthernetClientクラスのメソッドは実際には、「2」で作成したオブジェクトのメソッドを呼び出します。例えば、「client」というEthernetClient型のオブジェクトを「2」で作成したとすると、EthernetClient::connect()は、スケッチ上では、client.connect()となります。

以下にhttp://www.arduino.cc/にアクセスして、トップページを取得するサンプルプログラムを示します。当然ですが取得できるのは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
#include <SPI.h>
#include <Ethernet.h>
 
byte mac_address[] = {
  0x90, 0xA2, 0xDa, 0x0D, 0xD2, 0xEF
};
 
EthernetClient client;
 
void setup() {
  Serial.begin(9600);
 
  if (Ethernet.begin(mac_address) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP.");
    for(;;)
      ;
  }
 
  Serial.print("connecting...");
  if (client.connect("www.arduino.cc", 80)) {
    Serial.println("done.");
    // Make a HTTP request:
    client.println("GET / HTTP/1.1");
    client.println("Host: www.arduino.cc");
    client.println("Connection: close");
    client.println();
  } 
  else {
    Serial.println("failed.");
  }
}
 
void loop()
{
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }
 
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
    for(;;)
      ;
  }
}

EthernetClient()型の変数clientを定義します。setup()とloop()の両方で利用するため、グローバル変数として定義しています。

setup()では、前述した方法でEthernet ShieldにIPアドレス等を割り当てています。DHCPサーバ経由でIPアドレスを取得しているので、直接IPアドレスを指定したい場合は、適宜修正してください。

EthernetClient::connect()を使ってサーバに接続します。引数は接続先のホスト名とポートです。ホスト名ではなくIPアドレスで指定する場合は、IPAddress型の変数を指定します。

次に、EthernetClient::print()を用いてサーバに文字列を送信します。今回は、HTTP/1.1のGETメソッドと必要なほぼ最小限のヘッダ情報を送信しています。

その後、loop()内で、EthernetClient::available()で受信データがあるかを確認し、データがある場合は、EthernetClient::read()でデータを受信します。最後に、EthernetClient::connected()でコネクションの有無を確認しています。

サーバ編

TCPのサーバは以下の手順で実現することができます。

  1. IPアドレス等設定
  2. サーバ用オブジェクト作成
  3. クライアントからの接続準備(listen相当)
  4. クライアントからの接続待機(accept相当)
  5. データの受送信

以下に、Webサーバとしてアナログ入力の値をWebブラウザに表示するためのスケッチを示します。このスケッチは、チュートリアルにあるスケッチです。

EthernetServer型の変数serverを定義します。setup()とloop()の両方で利用するため、グローバル変数として定義しています。

setup()では、前述した方法でEthernet ShieldにIPアドレス等を割り当てています。IPアドレスを直接指定しているため、DHCPサーバ経由でIPアドレスを取得したい場合は、適宜修正してください。ただし、サーバ用途では毎回IPアドレスが変更されるのは不便なので、IPアドレスを直接指定するのがいいと思います。

EthernetServer::begin()を使ってクライアントからの接続準備を行います。引数はありません。

次に、EthernetServer::available()を使ってクライアントからの接続を待機します。接続がある場合は否ゼロの値が返却されます。返却されるのはEthernetClient型のオブジェクトです。

EthernetServer::available()を呼び出した際に返却されたEthernetClient型のオブジェクトを利用して、クライアントとのデータの受送信を行います。

EthernetServerクラスにもEthernetServer::write()というメソッドがありますが、これは、現在接続されているすべてのクライアントに対してデータを送信するためのメソッドです。

 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
#include <SPI.h>
#include <Ethernet.h>
 
// MACアドレスの定義
byte mac_address[] = {0x90, 0xA2, 0xDA, 0x0D, 0xD2, 0xEF};
 
// IPアドレスの定義
byte ip_address[] = {192, 168, 11, 200};
 
// サーバ用オブジェクト作成
EthernetServer server(80);
 
void setup() {
  Serial.begin(9600);
 
  // MACアドレス、IPアドレスを設定する。
  Ethernet.begin(mac_address, ip_address);
  // クライアントからの接続準備
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
}
 
void loop() {
  // クライアントからの接続待機
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    boolean currentLineIsBlank = true;
    // データの受送信
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        if (c == '\n' && currentLineIsBlank) {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          client.println("<meta http-equiv=\"refresh\" content=\"5\">");
          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') {
          currentLineIsBlank = true;
        } 
        else if (c != '\r') {
          currentLineIsBlank = false;
        }
      }
    }
    delay(1);
    client.stop();
    Serial.println("client disonnected");
  }
}

まとめ

Ethernetクラス、EthernetClientクラス、EthernetServerクラスの関係は以下の通りです。

クラス 用途
Ethernet IPアドレス等を設定する。
EthernetClient サーバへの接続要求を取り扱う。
クライアント・サーバ間のデータ送受信を行う。
EthernetServer クライアントからの接続要求を取り扱う。

以下に簡単な関連図を示します。

バージョン

Hardware:Arduino Uno/Arduino Due
Software:Arduino 1.0.4/Arduino 1.5.2

最終更新日

November 1, 2022

inserted by FC2 system