Arduinoに接続したセンサーの値をパソコンに表示したり、パソコンからLEDのON/OFFをコントロールします。どのようにするかというと、Arduinoをwebサーバーとして動かし、パソコンからアクセスできる簡単なHTMLページを作ります。LAN用のルーターとイーサネットシールドをイーサネットケーブルで接続して使用します。イーサネットシールドは、WIZnet社のTCP/IP処理専用モジュールのW5500を搭載したシールドがよく用いられます。扱い易いおすすめのシールドは、Arduino イーサネットシールド2で、Micro SDカードリーダとしても使用できます。
LEDと可変抵抗の配線図
LEDと可変抵抗を接続します。図2はArduino Unoから直接配線している図になっていますが、実際にはArduino UNOの上にイーサネットシールドを載せてあるので、ジャンパー線はイーサネットシールドのソケットにさすことになります。
イーサネットシールドのRJ45(LAN)コネクタとルーターをLANケーブルで接続します。ルーターを使わずに直接パソコンに接続することもできますが、PCは通常DHCPサーバー機能を持っていません。DHCPサーバ機能とは、LANに接続した機器にIPアドレスなどのネットワーク設定を自動で割り振る機能のことです。この場合は手動でIPアドレスを割り当てることになります。
IoTサーバーの動作説明
図3のようなHTML(web)ページに可変抵抗の読み取り値を表示し、リンクをクリックすることでLEDの点灯・消灯を切り替えられるようにします。これはwebサイトのアドレスに続くリンクでサーバーにリクエストします。「?on」を続ければLEDが点灯、「?off」を続ければLEDが消灯します。アドレス・サーバー名・ファイル名に続けて命令などの追加のデータを送信する場合、ウェブリクエストでは一般的に「?」(疑問符)をつけて表記します。
この例ではサーバーアドレスが「192.168.1.18」とすると、webページ上のリンク「LEDをONにする」は「192.168.1.18/?on」を、「LEDをOFFにする」は「192.168.1.18/?off」をそれぞれリクエストします。Arduinoウェブサーバーがこのリクエストを受け取り、内容に応じて動作します。
対応するHTMLソースは以下の通りです。簡略化のため、本来あるべき<head>、<body>の部分を省略していますが、ウェブブラウザでは正しく表示することができます。本来のHTMLやウェブデザインに関心があれば、HTMLについてのウェブページや書籍を参考にしてください。
<!DOCTYPE HTML> <html> アナログ値:240 <br><br> <a href='?on'>LEDをONにする</a> <br><br> <a href='?off'>LEDをOFFにする</a> </html>
HTMLはタグという呼ばれるコマンドを使ってwebページを組み立てていきます。タグは<>で囲まれています。<br>は改行を行うタグ、<a>はクリック可能なリンクを作成するタグです。本来はリンクしたいアドレスをすべて記載しますが、リンク先が同じサーバー上の場合にはアドレス部分を省略することができます。
このHTMLソースを生成し、LEDのON/OFFの切り替えリクエストに対応するスケッチが必要になります。Arduinoには「Ethernet.h」というライブラリが最初からインストールされています。
IoTサーバーのプログラム
次のスケッチをアップロードします。
#define LEDPIN 3 #define ANALOGPIN A0 #include "SPI.h" //(1) #include "Ethernet.h" byte MAC[] = {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56}; //(2) EthernetServer Server(80); //(3) EthernetClient Client; String Request; void setup() { pinMode(LEDPIN, OUTPUT); Serial.begin(115200); //(4) delay(100); Serial.println("My IP-Adresse: "); Ethernet.begin(MAC); //(5) delay(3000); Serial.println(Ethernet.localIP()); //(6) Server.begin(); //(7) } void loop() { Client = Server.available(); //(8) if (Client) { Serial.println("New Client"); boolean blankLine = true; while (Client.connected()) //(9) { if (Client.available()) //(10) { char c = Client.read(); if (Request.length() < 100) //(11) Request += c; if (c == '\n' && blankLine) //(12)(13) { Serial.print("Request from Client: "); Serial.println(Request); if(Request.indexOf("on")>0) //(14) digitalWrite(LEDPIN,HIGH); //(15) if(Request.indexOf("off")>0) //(16) digitalWrite(LEDPIN,LOW); // HTTP Header 200 an den Browser schicken Client.println("HTTP/1.1 200 OK"); //(17) Client.println("Content-Type: text/html"); Client.println("Connection: close"); //(18) Client.println("Refresh: 2"); //(19) Client.println(); //(20) Client.println("<!DOCTYPE HTML>"); //(21) Client.println("<html>"); Client.print("アナログ値:"); Client.print(analogRead(ANALOGPIN)); Client.println("<br><br>"); Client.println("<a href='?on'>LEDをONにする</a>"); Client.println("<br><br>"); Client.println("<a href='?off'>LEDをOFFにする</a>"); Client.println("</html>"); Request = ""; //(22) break; //(23) } if (c == '\n') //(24) blankLine = true; else if (c != '\r') blankLine = false; } } delay(1); //(25) Client.stop(); Serial.println("client disconnected."); Serial.println(""); } Ethernet.maintain(); //(26) }
スケッチの解説
(1) イーサネットシールドとの通信はSPIで行うので、SPIライブラリを読み込みます。
(2) 通常、ネットワークに接続して使うデバイスはMACアドレスという識別用のアドレスが割り振られていますが、安価な互換シールドにはMACアドレスがありません(Arduinoイーサネットシールド2はMACアドレスが書かれたシールがシールドに貼られています)。MACアドレスはLAN接続する上で必ず必要な物なので手動で設定します。MACアドレスの最初の3バイトはデバイスの製造元を識別するもので、適当な値を入れてしまうと「どのメーカーにも割り当てられていないアドレス」になってしまう可能性があります。接続するルーターやハブの中には、「どのメーカーにも割り当てられていないアドレス」からの接続を拒否するものもあります。割り当てのあるアドレスはwebページ(https://uic.jp/mac/)で調べられます。後半3バイトは任意のアドレスを使用します。
(3) サーバー用・クライアント用のオブジェクトを、ライブラリのクラスから作成します。サーバーとして作成する場合は、リクエストを受け取るポートの番号を引数として渡します。暗号化のされていないwebサイト(httpサイト)では、常に「80」番を使用します。
(4) 今回の例ではシリアルモニタでやり取りするデータ量が多くなっているので、シリアル通信の速度を高速にしています。シリアルモニタの設定も変える必要があります。今回の例ではシリアル通信は動作確認のために取り入れているだけなので、省略することもできます。
(5) 「Ethernet.begin()」関数にMACアドレスを渡すネットワーク接続を開始します。オプションで、2つ目の引数に静的IPアドレスを渡すこともでき、パソコンと直接接続して使用する場合に必要になります。2つ目の引数がない場合には、DHCP経由で自動的にIPアドレスを取得します。
(6) DHCPで取得したIPアドレスを表示します。
(7) 「Server.begin()」関数を実行することでウェブサーバーとして動作し始めます。
(8) リクエストが届いた場合、「Server.available()」関数はそのリクエストを発したクライアントを返します。リクエストがなければfalseを返します。クライアントに応答を返すときに、クライアントのIPアドレスとポートを割り当てることができるようになります。
(9) クライアントとの接続が続いている限り、while文の中を繰り返します。
(10) クライアントからリクエストが届いているか確認します。この段階ではクエリ(リクエスト)の内容は読み込んでいません。
(11) クエリを「Client.read()」関数で1バイトずつ読み取り、文字列オブジェクトRequestに追加していきます。メモリを節約するため、クエリは100バイトまでに制限しています。
(12) クライアントはリクエストの終わりを「空行」を送ることで表現します。「\n」は改行するという意味のヌル文字(実際には表示されない文字)を表し、変数「blankLine」は前回受け取ったリクエストが実際には空行だった場合にtrueになります。この後、リクエストを1バイト(≒1文字)ずつ受け取りながら、リクエストを組み立てていきます。改行コードがその終わりを意味するということです。
(13) リクエストが終了した場合は処理を開始します。
(14) 「indexOF()」関数は文字列オブジェクトの中に引数で渡した文字列があるかどうかを検索する関数です。戻り値は引数の文字列が見つかればその文字列、見つからなければ-1となります。ここではリクエストの文字列に「on」という文字が含まれているかどうかを確認しています。
(15) リクエストに文字列「on」が含まれていればLEDを点灯させます。
(16) 同様に、リクエストに「off」という文字列が含まれていればLEDを消灯します。
(17) リクエストが成功したことをクライアントに返信します。HTTP 200というのが、リクエスト成功という意味です。
(18) 返信後、接続を終了します。
(19) アナログ値を更新するため、2秒ごとに自動でページを再読み込みするよう指示します。
(20) 空行は、ヘッダーデータの終わりとHTTPソースの始まりを示しています。
(21) 先に示したHTMLタグを順番に送信していきます。
(22) 送信終了後、次のリクエストを受け付けられるように、文字列Requestを空にしておきます。
(23) 「break()」関数は条件分岐から条件に関わらず抜け出す命令です。ここではwhileループから抜けて、(25)の行にジャンプします。
(24) 受け取ったリクエストのデータcが改行コード「\n」だった場合に、変数blankLineをtrueに設定します。改行コードではない場合にはfalseに設定します。
(25) 「Client.print()」や「Client.println()」といったコマンドを送った後に、イーサネットモジュールの反応が遅れる場合があるため、1ミリ秒動作を停止した後で接続を終了します。
(26) DHCP経由でIPアドレスを取得した場合、定期的に「Ethernet.mentain()」関数を呼び出す必要があります。DHCPによって割り振られたIPアドレスには有効期限(通常は数時間)があり、期限切れ後にDHCPサーバーが別の機器に同じIPアドレスを割り振ってしまう可能性があります。「Ethernet.mentain()」関数は、DHCPサーバーに取得したIPアドレスの有効期限を延長する連絡をさせる命令です。今回の例ではloop()文を実行するたびに「Ethernet.mentain()」を呼び出していますが、実際に連絡をするのはIPアドレスの有効期限が迫っているときのみです。
スケッチのアップロード後、シリアルモニタを開くとDHCPで割り当てられたIPアドレスを確認できます。このIPアドレスをwebブラウザのアドレスバーに入力するだけで接続でき、webサイトが開きます。同時に、シリアルモニタでリクエストの内容を確認することもできます。
コメントを残す