「Arduinoの基礎 - Ethernet」の版間の差分

提供:MochiuWiki : SUSE, EC, PCB

📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
移転先はhttps://mochiu.net/index.phpです。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

166行目: 166行目:
ホストPCのソフトウェアでは、ONボタンを押下した時はArduinoにデータ"S:1"を送信、OFFボタンを押下した時にデータ"S:0"を送信する。<br>
ホストPCのソフトウェアでは、ONボタンを押下した時はArduinoにデータ"S:1"を送信、OFFボタンを押下した時にデータ"S:0"を送信する。<br>
このようなクライアントとサーバ間のデータ通信の取り決めのことを、プロトコルという。<br>
このようなクライアントとサーバ間のデータ通信の取り決めのことを、プロトコルという。<br>
[[ファイル:Arduino Ethernet 1.jpg|フレームなし|中央]]
<br>
<br>
次に、ArduinoにEthernetシールドを接続する。<br>
次に、ArduinoにEthernetシールドを接続する。<br>
下図に、Ethernetシールドの表面と裏面、接続図を示す。<br>
下図に、Ethernetシールドの表面と裏面、接続図を示す。<br>
 
[[ファイル:Arduino Ethernet 2.jpg|フレームなし|中央]]
<br>
<br>
下図に、ArduinoとLEDの接続を示す。<br>
下図に、ArduinoとLEDの接続を示す。<br>
8番ピンのデジタル出力を使用している。<br>
8番ピンのデジタル出力を使用している。<br>
ここでは、抵抗は470[Ω]を使用しており、流れる電流は<math>I = \frac{5 \mbox{[V]} - 2.1 \mbox{[V]}}{470 [\Omega]} = 6 \mbox{[mA]}</math>である。<br>
ここでは、抵抗は470[Ω]を使用しており、流れる電流は<math>I = \frac{5 \mbox{[V]} - 2.1 \mbox{[V]}}{470 [\Omega]} = 6 \mbox{[mA]}</math>である。<br>
 
[[ファイル:Arduino Ethernet 3.jpg|フレームなし|中央]]
<br>
<br>
以下に、Arduino Unoのソースコードを示す。<br>
以下に、Arduino Unoのソースコードを示す。<br>
181行目: 182行目:
その他のデータを受信した場合、クライアントがそのデータを捨てる。<br>
その他のデータを受信した場合、クライアントがそのデータを捨てる。<br>
1バイトずつにデータを取得するため、下図のような状態遷移となる。<br>
1バイトずつにデータを取得するため、下図のような状態遷移となる。<br>
 
[[ファイル:Arduino Ethernet 4.jpg|フレームなし|中央]]
<br>
<br>
MACアドレスは、一意性のある6バイトを設定する。(ここでは、0xDE 0xAD 0xBE 0xEF 0xF0 0x0Dとしている)<br>
MACアドレスは、一意性のある6バイトを設定する。(ここでは、0xDE 0xAD 0xBE 0xEF 0xF0 0x0Dとしている)<br>
282行目: 283行目:
ここでは、Windowsで利用可能なC#と.NET Frameworkを利用して、上述のプロトコルをサポートするTCPクライアントを作成する。<br>
ここでは、Windowsで利用可能なC#と.NET Frameworkを利用して、上述のプロトコルをサポートするTCPクライアントを作成する。<br>
下図のように、ソフトウェアのUIにおいて、ArduinoのIPアドレスとポート番号を指定できるようにしている。<br>
下図のように、ソフトウェアのUIにおいて、ArduinoのIPアドレスとポート番号を指定できるようにしている。<br>
 
[[ファイル:Arduino Ethernet 5.jpg|フレームなし|中央]]
<br>
<br>
以下に、.NET FrameworkのTcpClientクラスを使用したソースコードを記述する。<br>
以下に、.NET FrameworkのTcpClientクラスを使用したソースコードを記述する。<br>

2020年12月3日 (木) 22:43時点における版

概要



Ethernetシールド

DHCPの利用

ArduinoでEthernetシールド等を使用する場合、プログラムで明示的にIPアドレスを指定するのではなく、
DHCPで自動的にIPアドレスの割り当てを行う方法を記載する。

Arduinoの標準のEthernetライブラリを使用することで、簡単に実現できる。
これは、Ethernet.begin()を呼び出す時、MACアドレスのみを指定することで(IPアドレスは指定しない)、DHCPからIPアドレスを自動で取得できる。

ここでは、Arduino UnoにEthernetシールドを接続している。
以下の例では、シリアルポートに結果を出力するために、Serial.begin()println()を呼び出している。

 #include <Ethernet.h>
 
 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xF0, 0x0D };
 
 void setup()
 {
    Serial.begin(9600);
 
    Ethernet.begin(mac);
    Serial.println(Ethernet.localIP());
 }
 
 void loop()
 {
 
 }


Webサーバの実装

このセクションでは、Arduinoの標準のEthernetライブラリを使用して、単純なWebサーバを実装する。
簡単なWebサーバの実装例はExampleに含まれている。

以下の例では、URIを確認して、HTTPステータスコード 200 OKおよび404 File Not Foundを返している。
Webサーバが必要な評価試験において、以下のようなサンプルコードを動作させるとよい。

IPアドレスはDHCPから自動で取得しており、手動で設定する場合は、Ethernet.begin()で指定する。
また、IPアドレスはシリアルポートに出力しており、
WebブラウザからこのWebサーバに接続する時は、シリアルモニタに表示されたIPアドレスへリクエストを送る。

リクエストラインのみを確認するので、その行だけ特別にバッファ(変数reqLine)にコピーしている。
クライアントからのリクエストを読み込みんだ後、バッファを確認してメソッドとURIを取得する。
もし、長いリクエストが来た場合、途中まで読み込む。

Content-Lenghをセットしない代わりに、データ区切りとしてConnection closeする。
効率は悪いが、単一クライアントのみ接続が可能なので、1つずつのリクエスト処理は短くする必要がある。

 
 #include <Ethernet.h>
 
 byte mac[] = { 0xDE,0xAD,0xBE,0xEF,0xF0,0x0D };
 EthernetServer server(80);
 
 void setup()
 {
    Serial.begin(9600);
 
    Ethernet.begin(mac);
    Serial.println(Ethernet.localIP());
 
    server.begin();
 }
 
 void loop()
 {
    EthernetClient client = server.available();
    if(client)
    {
 
       Serial.println("*** New Client ***");
 
       boolean currentLineIsBlank = true;
       boolean requestLineRead = false;
       char reqLine[64] = {0};
       char *p = reqLine;
       char *method = NULL;
       char *uri = NULL;
       const char delims[2] = " ";
 
       while(client.connected())
       {
          char c = client.read();
          // Serial.write( c );
 
          // Query
          if(!requestLineRead && (p - reqLine ) < (sizeof(reqLine) - 1) && c != '\r' && c != '\n' )
          {
             *p = c;
             p++;
          }
 
          if(c == '\n' && currentLineIsBlank)
          {
             // Parse Query
             p = strtok(reqLine, delims);
             while( p )
             {
                if(!method)
                {
                   method = p;
                }
                else if(!uri)
                {
                   uri = p;
                }
                p = strtok(NULL, delims);
             }
 
             // Send Response
             if(!strcmp(method, "GET") && !strcmp(uri, "/pic"))
             {
                client.println("HTTP/1.1 200 OK");
                client.println("Content-Type: text/plain");
                client.println("Connection: close");
                client.println();
                client.println("OK!");          
             }
             else
             {
                client.println("HTTP/1.1 404 Not Found");
                client.println("Content-Type: text/plain");
                client.println("Connection: close");
                client.println();
                client.println("File Not Found");
             }
        
             break;
          }
 
          if(c == '\n')
          {
             currentLineIsBlank = true;
             if(!requestLineRead)
             {
                requestLineRead = true;
             }
          }
          else if(c != '\r')
          {
             currentLineIsBlank = false;
          }
       }
 
       delay( 10 );
       client.stop();
       Serial.println("Client disconnected");
    }
 }


Arduinoの遠隔操作

Arduinoのメリットの1つに、各種シールドと開発環境における各種ライブラリの存在が挙げられる。

例えば、Arduino UnoをLANに接続するには、Ethernetシールドのみ準備することで、Arduino Unoにネットワーク機能を追加することができる。
そして、そのネットワーク機能はハードウェアの違いを意識せずに、Ethernetライブラリを使用することで実現できる。
さらに、プログラミングインターフェイスとして、ソケットよりも抽象化されたサーバオブジェクトとクライアントオブジェクトが用意されている。

このセクションでは、ArduinoとEthernetシールドを使用したLED遠隔操作ソフトウェアを作成する。
ホストPC上のソフトウェアONボタンまたはOFFボタンを押下することで、Arduinoに接続されたLEDを制御する。
また、ArduinoとホストPCは、有線LANケーブルを使用してルータまたはハブに接続する。

Arduinoのソースコードでは、TCPポート8888番をリスニングしており、データ"S:1"を受信するとLEDを点灯、"S:0"を受信するとLEDを消す。
ホストPCのソフトウェアでは、ONボタンを押下した時はArduinoにデータ"S:1"を送信、OFFボタンを押下した時にデータ"S:0"を送信する。
このようなクライアントとサーバ間のデータ通信の取り決めのことを、プロトコルという。

Arduino Ethernet 1.jpg


次に、ArduinoにEthernetシールドを接続する。
下図に、Ethernetシールドの表面と裏面、接続図を示す。

Arduino Ethernet 2.jpg


下図に、ArduinoとLEDの接続を示す。
8番ピンのデジタル出力を使用している。
ここでは、抵抗は470[Ω]を使用しており、流れる電流はである。

Arduino Ethernet 3.jpg


以下に、Arduino Unoのソースコードを示す。

データ"S:1"でLEDを点灯、データ"S:0"でLEDを消す。
その他のデータを受信した場合、クライアントがそのデータを捨てる。
1バイトずつにデータを取得するため、下図のような状態遷移となる。

Arduino Ethernet 4.jpg


MACアドレスは、一意性のある6バイトを設定する。(ここでは、0xDE 0xAD 0xBE 0xEF 0xF0 0x0Dとしている)
ネットワークアドレスは192.168.1.0/24を想定しており、ArduinoのIPアドレスは192.168.1.10と固定する。
また、ポート番号は、TCP 50000番とする。

 #include <EthernetServer.h>
 #include <Ethernet.h>
 #include <EthernetClient.h>
 #include <Dhcp.h>
 #include <Dns.h>
 #include <EthernetUdp.h>
 
 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xF0, 0x0D };
 byte ip[] = { 192, 168, 1, 10 };
 EthernetServer server(50000);
 
 const int STATE_INIT = 0;
 const int STATE_1 = 1;
 const int STATE_2 = 2;
 const int STATE_3 = 3;
 
 const int PIN_LED = 8;
 
 void setup()
 {
    Ethernet.begin( mac, ip );
    server.begin();
 
    pinMode( PIN_LED, OUTPUT );
    digitalWrite( PIN_LED, LOW );
 }
 
 void loop()
 {
    EthernetClient client = server.available();
    int state = STATE_INIT;
    int i;
 
    if(client)
    {
       while(client.connected())
       {
          while( ( i = client.read() ) != -1 )
          {
             if( state == STATE_INIT )
             {
                if( i == 'S' )
                {
                   state = STATE_1;
                }
                else
                {
                   client.stop();
                   break;
                }
             }
             else if( state == STATE_1 )
             {
                if( i == ':' )
                {
                   state = STATE_2;
                }
                else
                {
                   state = STATE_INIT;
                   client.stop();
                   break;
                }
             }
             else if( state == STATE_2 )
             {
                if( i == '1' )
                {
                   switch_led(1);
                   client.print("OK");
                }
                else if( i == '0' )
                {
                   switch_led(0);
                   client.print("OK");
                }
 
                state = STATE_INIT;
                client.stop();
                break;
             }
          }  
       }
    }
 }
 
 void switch_led(int on)
 {
    digitalWrite( PIN_LED, on ? HIGH : LOW );  
 }


最後に、ホストOSのソフトウェアを作成する。
ここでは、Windowsで利用可能なC#と.NET Frameworkを利用して、上述のプロトコルをサポートするTCPクライアントを作成する。
下図のように、ソフトウェアのUIにおいて、ArduinoのIPアドレスとポート番号を指定できるようにしている。

Arduino Ethernet 5.jpg


以下に、.NET FrameworkのTcpClientクラスを使用したソースコードを記述する。

 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Data;
 using System.Drawing;
 using System.Linq;
 using System.Net.Sockets;
 using System.Text;
 using System.Windows.Forms;
 
 namespace LightApp
 {
    public partial class Form1 : Form
    {
       public Form1()
       {
          InitializeComponent();
       }
 
       private void button1_Click( object sender, EventArgs e )
       {
          SendCommand( true );
       }
 
       private void button2_Click( object sender, EventArgs e )
       {
          SendCommand( false );
       }
 
       void SendCommand( bool on )
       {
          try
          {
             var port = int.Parse( textBox2.Text );
             using(var client = new TcpClient( textBox1.Text, port ))
             {
                Byte[] data = System.Text.Encoding.ASCII.GetBytes( string.Format( "S:{0}", on ? "1" : "0" ) );
                using(NetworkStream stream = client.GetStream())
                {
                   stream.Write( data, 0, data.Length );
                   var data = new Byte[256];
                   var responseData = String.Empty;
                   Int32 bytes = stream.Read( data, 0, data.Length );
                   responseData = System.Text.Encoding.ASCII.GetString( data, 0, bytes );
                }
             }
          }
          catch(Exception ex)
          {
             MessageBox.Show( ex.Message, "Switch", MessageBoxButtons.OK, MessageBoxIcon.Error );
          }
       }
    }
 }