ArduinoのEthernetライブラリでLAN通信する

組み込みエンジニア
本記事はプロモーションが含まれています。

こんにちは、ENGかぴです。

Arduino UNO専用のETHERNET SHIELDを実装するとEthernetライブラリを使用してLAN通信ができます。DHT20モジュールから取得した温湿度の情報をHTTPで応答するWebサーバーを実装して動作確認を行いました。

DHT20モジュールはGrove-Temperature & Humidity Sensor V2.0(DHT20)(Seeed Studio) 及びETHERNET SHIELDはArduino Ethernet Shield 2を使用しています。

ETHERNET SHIELDはアイキャッチ画像のようにArduino UNOに差し込むだけで使用することができます。Arduino UNO(以下Arduinoとします。)を対象とします。Arduinoのライブラリを使用して動作確認したことをまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Ethernetライブラリを使用する

Arduino Ethernet Shield Rev2を含む全体構成
Arduino Ethernet Shield Rev2を含む全体構成

ETHERNET SHIELDはイーサネットコントローラーにW5500が使用されておりSPI通信をイーサネットに変換することができます。Arduino環境でイーサネットのプロトコルであるTCP/IPやUDP/IPによる通信を構築することができます。

本記事ではパソコンに搭載(付属)しているLANポートとETHERNET SHIELDのLANポートをケーブルで接続した状態でパソコンのブラウザーからArduinoに実装したWebサーバーに接続要求を送ります。

Arduinoは接続要求を受け付けるとDHT20モジュールから取得した温湿度データをHTMLデータで応答します。HTMLデータにブラウザーのリフレッシュ指定を追加して等間隔で接続要求を送信します。

EthernetライブラリはArduino UNO用に標準搭載されておりスケッチ例が各種準備されています。本記事ではスケッチ例の「WebServer」を流用して動作確認を行っています。

Ethernetライブラリのインクルードからを初期化及び関数の使用方法を説明します。

スポンサーリンク

ライブラリの準備と初期化

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

byte mac[] = {
  0x83, 0x61, 0x0A, 0xAE, 0x73, 0xA8
};
IPAddress ip(192, 168, 1, 1);
EthernetServer server(80);

void setup() {

  Ethernet.begin(mac, ip);//mac及びipで初期化
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");//イーサーネットシールドの接続が確認できない場合表示
    while (true) {
      delay(1);
    }
  }

  if (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable is not connected.");
  }

  server.begin(); //サーバースタート
}

EthernetライブラリはSPI通信を使用するためEthernet.hとSPI.hの2つのライブラリをインクルードする必要があります。

イーサーネットコントローラーは固有のMACアドレスが割り振られているため6バイトの値を配列で準備します。ETHERNET SHIELDの裏面にMACアドレスが貼り付けてあります。

IPAddressクラスの変数としてipをインスタンス化し、引数をIPアドレスを指定します。例ではIPアドレスに「192.168.1.1」を指定しています。

EthernetServerクラスの変数としてserverをインスタンス化(serverオブジェクト)し、引数にポート番号を指定します。例ではHTTPデータ用のデフォルトのポートの80を指定しています。

Ethernetオブジェクトのbegin()関数でイーサーネットの初期化を行います。第1引数にMACアドレスを指定します。第2引数にIPアドレスを指定します。

hardwareStatus()関数はETHERNET SHIELDの接続を確認します。例では接続が確認できなければETHERNET SHIELDが確認できるまでループさせるようにしています。

linkStatus()関数はイーサーネットに接続状態を確認します。ケーブルの抜き差しでリンクのステータスが切り替わるため特に意識して接続状態を確認する必要はありません。

serverオブジェクトのbegin()関数でWebサーバーをスタートします。

PR:スキマ時間を有効に!スマホで学べる人気のオンライン資格講座【スタディング】まずは気になる講座を無料で体験しよう!

クライアント接続の確認

EthernetClient client = server.available();//サーバーの状態を確認

if (client) { //クライアントが存在するか
  while (client.connected()) {
    if (client.available()) {//受信データが有
      char c = client.read(); //データ読み込み
      //データを読み込んだ後にクライアントへの応答
    }
  }
  client.stop(); //クライアント情報を破棄する
}

EthernetClientクラスの変数としてclientでインスタンス化(clientオブジェクト)し、serverオブジェクトのavailable()関数でサーバーの状態を確認します。クライアントが存在する場合はclientオブジェクトのconnected()関数で接続状態を確認します。

ケーブルの断線などで強制的にクライアントが切断した場合などはTCP/IPプロトコルの仕様でパケットが残っていることがあります。クライアント情報が破棄されるまで処理が遅延されることがあるので注意が必要です。

clientオブジェクトでクライアントからの受信データをavailable()関数で確認します。受信データがあればread()関数でクライアントから受信したデータを読み込みます。

データを読み込んだ後はクライアントにHTMLデータで応答します。応答後はstop()関数でクライアントの情報を破棄します。

クライアントへの応答

 client.println("HTTP/1.1 200 OK");
 client.println("Content-Type: text/html");
 client.println("Connection: close");
 client.println("Refresh: 5");
 client.println();
 client.println("<!DOCTYPE HTML>");
 client.println("<html>");
 //温湿度のデータ
 client.print("Humidity: ");
 client.print(temp_humi[0]);
 client.print(" %\t");
 client.print("Temperature: ");
 client.print(temp_humi[1]);
 client.println(" *C");
 client.println("<br />");
 client.println("</html>");

clientオブジェクトのprint()関数またはprintln()関数でHTMLデータのヘッダーとコンテンツを送信します。HTMLのヘッダーのRefresh: 5は5秒毎にブラウザーに表示しているデータを更新する指定です。

<html>より下は温湿度のデータを送信しています。温湿度のデータを送信した後は</html>でHTMLデータの終了を通知します。

スポンサーリンク

DHT20モジュールからデータを取得

Arduino IDEでDHTライブラリを追加してDHT20モジュールから温湿度のデータを取得します。Arduino IDEのライブラリマネージャの検索欄にgrove dht(ライブラリ名をそのまま入力しても良い)を入力するとライブラリの候補が表示されます。

候補の中からGrove Temperature And Humidity Sensorをインストールします。使用例は以下の通りです。

#include <DHT.h>
#include <Grove_Temperature_And_Humidity_Sensor.h>
#include <Wire.h>

DHT dht(DHT20); //DHT20を指定してインスタンス化
float temp_humi[2];

void setup() {

  Serial.begin(115200);
  Wire.begin();
  dht.begin();
}

void loop() {
  
  dht.readTempAndHumidity(temp_humi);//データを取得
  Serial.print("Humidity: ");
  Serial.print(temp_humi[0]);
  Serial.print(" %\t");
  Serial.print("Temperature: ");
  Serial.print(temp_humi[1]);
  Serial.println(" *C");
  delay(2000);
}

Arduino IDEのスケッチタブの「ライブラリをインクルード」からGrove Temperature And Humidity Sensorを選択します。エディターにDHT.h及びGrove_Temperature_And_Humidity_Sensor.hが追加されますが、DHT.hのみのインクルードでも問題ありません。DHT20はI2C通信を使用するのでWire.hも含めてインクルードします。

DHTクラスの変数としてdhtに引数を指定してインスタンス化(dhtオブジェクト)します。DHT20モジュールを使用するためDHT20を指定します。

DHT.hを選択した状態で「Go to Definition」をクリックするとDHT.hが開けるので対象のDHTモジュールのタイプを定義を確認することができます。

Wire.begin()でI2C通信の初期化を行い、dhtオブジェクトのbegin()関数でDHT20の初期化を行います。

readTempAndHumidity()関数を使用すると温湿度のデータを取得することができます。引数に取得したデータを格納するアドレスを指定します。

使用例のようにデータを取得した場所で関数をコールするだけで簡単に測定値が取得できるため手軽に動作確認できます。

広告
漠然としたキャリア形成の不安を打ち破る!

動作確認

Ethernetライブラリの動作確認の回路図
Ethernetライブラリの動作確認の回路図

ArduinoにETHERNET SHIELDを接続します。LANケーブルをパソコンに接続してイーサネット通信を行います。電源を入れるとDHT20の測定が開始します。パソコンのブラウザーからArduinoのIPアドレスを指定するとDHT20の測定データを含めたHTMLデータで応答します。

動作確認はクライアント側のイーサーネットの設定が必要です。設定の一例を説明します。Windows11を対象とします。

スタート画面(窓のアイコン)から設定の歯車を選択します。ネットワークとインターネットからネットワークの詳細設定項目を選択します。「ネットワークアダプター」で対象のイーサーネットを選択してIPアドレスなどの設定を行います。

ネットワークの詳細設定
ネットワークの詳細設定

動作確認はUSBハブを使用しておりイーサーネット2と表記されていますが、お使いの環境によってイーサーネットの番号は異なります。

「その他のアダプターオプション」の編集を選択するとイーサーネット2のプロパティが表示されます。プロパティから「インターネット プロトコル バージョン 4(TCP/IPv4)」を選択し、プロパティ(R)を選択します。

IPアドレスの設定(クライアント側)
IPアドレスの設定(クライアント側)

ArduinoのイーサーネットのIPアドレスを「192.168.1.1」に設定しているため同一のクラスのIPになるようにUSBハブのIPアドレスを設定します。例では「192.168.1.10」を指定しています。

Google Chromeなどのブラウザーを開いてアクセス先にArduinoのアドレス「192.168.1.1」を指定するとブラウザー(クライアント)からArduinoに対してリクエストを送信します。Arduinoから応答があるとブラウザーに温湿度のデータが表示されます。

ブラウザーの表示結果
ブラウザーの表示結果

ブラウザーの表示を確認すると湿度と温度のデータが表示されています。また、ブラウザーを開いたままにしていると5秒毎にリフレッシュされ温湿度のデータが更新されていることも確認できました。

PR:技術系の通信教育講座ならJTEX

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。コメントなど細かな部分で間違っていたりやライブラリの更新などにより動作しなくなったりする可能性があります。参考としてお使いいただければと思います。

#include <MsTimer2.h>
#include <DHT.h>
#include <Grove_Temperature_And_Humidity_Sensor.h>
#include <SPI.h>
#include <Ethernet.h>
#include <Wire.h>

#define TIME_UP 0
#define TIME_OFF -1
#define TIM_MEASURE 200

byte mac[] = {
  0x83, 0x61, 0x0A, 0xAE, 0x73, 0xA8
};

IPAddress ip(192, 168, 1, 1);
EthernetServer server(80);

int16_t timMeasure;
DHT dht(DHT20);
float temp_humi[2];

/* Local function prototypes */
void mainTimer(void);
void mainApp(void);

void setup() {

  Serial.begin(115200);
  Wire.begin();
  dht.begin();

  Ethernet.begin(mac, ip);
  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1);
    }
  }
  if (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable is not connected.");
  }
  // start the server
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());

  MsTimer2::set(10,mainTimer); //10msごとに関数へ遷移
  MsTimer2::start();
}

void loop() {

  mainApp();
}

/* タイマ管理(timer2がタイムアップするとコールされる) */
void mainTimer(void){

  if( timMeasure > TIME_UP ){
    --timMeasure;
  }
}

/* メイン処理 */
void mainApp(void){

  if( timMeasure == TIME_UP ){
    timMeasure = TIM_MEASURE;

    if (!dht.readTempAndHumidity(temp_humi)) {
      Serial.print("Humidity: ");
      Serial.print(temp_humi[0]);
      Serial.print(" %\t");
      Serial.print("Temperature: ");
      Serial.print(temp_humi[1]);
      Serial.println(" *C");
    } else {
      Serial.println("Failed to get temprature and humidity value.");
    }
  }

  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    bool currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);

        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("Connection: close");
          client.println("Refresh: 5");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          //温湿度のデータ
          client.print("Humidity: ");
          client.print(temp_humi[0]);
          client.print(" %\t");
          client.print("Temperature: ");
          client.print(temp_humi[1]);
          client.println(" *C");
          client.println("<br />");
          client.println("</html>");
        }
        if (c == '\n') {
          currentLineIsBlank = true;
        } else if (c != '\r') {
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    client.stop(); // close the connection:
    Serial.println("client disconnected");
  }
}

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

PR:テックジム:プログラミングの「書けるが先で、理解が後」を体験しよう!

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました