Pythonロボティクスコース レッスン 46

テーマ.12-1 Webサーバーの開発

Studuino:bitをWebサーバーとして立ち上げよう!

このレッスンで学ぶこと

このレッスンでは、Studuino:bitをWi-Fiのアクセスポイントにして構築したローカルネットワーク内で、Webサーバーとして動かす方法を紹介します。ローカルネットワークに接続したクライアントから届いたリクエストに応じてHTMLファイルのデータを送信するようにしてみましょう。

レッスンの後半では、Studuino:bitに内蔵されている各種センサの値を、手元にあるスマートフォンやタブレットからモニタリングできるアプリケーションを制作したWebサーバー上で実行します。

Studuino:bitをWi-Fiのアクセスポイントとして起動する方法

テーマ.11では、教室に設置されているWi-FiのアクセスポイントへStuduino:bitをクライアント(別名:ステーション)として接続し、インターネットから時刻や気象情報などを取得しました。Studuino:bitはWi-Fiのアクセスポイントとして起動させることもでき、独自のローカルエリアネットワーク(LAN)を構築することができます。

2. 1 Wi-Fiのアクセスポイントとして起動するプログラムの作成

Wi-Fiを利用するときは、networkモジュールのWLANクラスを使います。ステーションとしてWLANクラスのオブジェクトを作成するときは、コンストラクタの引数にnetwork.STA_IFを指定し、アクセスポイントとしたいときはnetwork.AP_IFを指定します。

network.WLAN(network.STA_IF)  # ステーションとしてWLANオブジェクトを作成
network.WLAN(network.AP_IF)  # アクセスポイントとしてWLANオブジェクトを作成

また、アクセスポイントとして立ち上げるためには、一般的なWi-Fiルーターと同じように、SSIDや認証パスワードなどのパラメータを設定しておく必要があります。この設定を行うためには、WLANクラスのconfig()メソッドに以下の引数を渡して実行します。

【 config()メソッドの引数 】

|引数名|データ型|説明|
|:—|:—|:—|
|essid|文字列|Wi-Fiのアクセスポイント名(SSID)|
|password|文字列|認証が必要な場合のパスワード|
|authmode|数値|認証モード|
|channel|数値|使用するWi-Fiのチャンネル|
|hidden|ブール値|essidの表示・非表示の設定|

authmodeでは、以下の番号で認証モードを設定します。これらの認証モードは「 WEP < WPA-PSK < WPA2-PSK 」の順に、よりセキュリティが強化されています。「0:認証なし」を設定した場合を除き、パスワード(password)が必要になります。

  • 0:認証なし
  • 1:WEP
  • 2:WPA-PSK
  • 3:WPA2-PSK
  • 4:WPA/WPA2-PSK

channelには、1~13の範囲でWi-Fiのチャンネルを指定します。Wi-Fiは電波どうしの干渉による通信速度の低下を防ぐために、2.4GHz帯や5GHz帯などWi-Fiに割り当てられた帯域幅をさらに複数のチャンネルで分割して利用しています。例えば、Studuino:bitをWi-Fiのアクセスポイントとして起動したときは「IEEE802.11g」という規格に従って無線通信が行われており、この規格では1ch~13chで2.4GHzの帯域幅が分割されています。各チャンネルは中心となる周波数から両側に11MHzの幅を使って通信を行います。

※ 2.4GHz = 2400MHz

hiddenではSSIDの表示/非表示が設定でき、Trueを指定すると、スマートフォンやタブレットから利用可能なWi-Fiのアクセスポイントを検索しても一覧に表示されなくなります。

それではここまでのことを踏まえた上で、以下のようにパラメータを設定して、Studuino:bitをWi-Fiのアクセスポイントとして起動するプログラムを書きましょう。コードはlaunch_ap()という名前の関数にまとめます。

【 config()メソッドの各引数に設定する値 】
教室で同時に複数のStuduino:bitをWi-Fiのアクセスポイントとして起動する場合は、SSIDが重ならないように名前付けをしてください。
引数設定値
essid"studuinobit"もしくは好きな文字列
password"Artecrobo2"もしくは好きな文字列
authmode4(WPA/WPA2-PSK)
channel11
hiddenFalse
【 Wi-Fiのアクセスポイントとして起動するためのサンプルコード 】

上のプログラムを実行したら、スマートフォンやタブレット端末のWi-Fi設定画面を開き、一覧に設定したSSIDが表示されていることを確認しましょう。

※ 以下はiPhone(iOS)の画面です。

そのSSIDを選択して、設定したパスワードを入力して接続を試みます。

スマートフォンやタブレットの画面上で無事に接続できたことを確認しましょう。
(※インターネット回線には接続されません)

※ スマートフォンを使用している場合に接続が安定しないときは、モバイル通信機能をOFFにすると接続が安定することがあります。

端末からの接続があると、Muのターミナルには、以下のようなメッセージが表示されます。

上のメッセージの4行目「I (6644771) tcpip_adapter: softAP assign IP to station,IP is: 192.168.4.2」の末尾に注目すると、192.168.4.2が接続した端末に自動的に割り当てられたIPアドレスになっています。このようにローカルエリアネットワークに接続した端末にIPアドレスなどの情報を自動で割り当てる仕組みを「DHCP(Dynamic Host Configuration Protocol)」といいます。
※最新のアンドロイド、IOSだと、違う表示がされます。

DHCPとは?

DHCPとは、「Dynamic Host Configuration Protocol(ダイナミック ホスト コンフィギュレーション プロトコル)」の頭文字をとった言葉で、コンピュータがネットワークに接続して通信する際に必要な設定情報を自動的に割り当てる機能です。

コンピュータが通信を行うには、IPアドレスをコンピュータ1台1台に割り当てることによって正しく通信が行えます。しかしすべてのコンピュータに手動でIPアドレスを割り当てるのは時間や手間がかかる上に、台数が増えれば増えるほど大きな業務負担となります。
この課題を解決するのがDHCPです。

DHCPとは?

DHCPのしくみ

DHCPでは「DHCPサーバー」「DHCPクライアント」の二つのしくみにより設定情報を割り当てています。

DHCPサーバーとは、ネットワークなどに接続したいコンピュータに、IPアドレスなど必要な情報を自動的に発行するサーバーです。

DHCPクライアントとは接続しようとするコンピュータに備わる機能です。コンピュータのユーザーは接続設定で内蔵されたDHCPクライアント機能を有効にします。すると、DHCPクライアントが適切な設定を取得してくれるため、設定を手動で行う必要がなくなります。

DHCPクライアントによるIPアドレス設定の流れ

DHCPクライアントによるIPアドレス設定の流れ

DHCPクライアントによりIPアドレスが自動的に設定されるまでの流れを解説します。

1.DHCPサーバーを探す

DHCPクライアントは、ネットワーク接続の際に、まずDHCPサーバーを探します。

2.IPアドレスの提案が返ってくる

探し当てられたDHCPサーバーはDHCPクライアントへ、IPアドレスの提案を返します。

3.DHCPサーバーにIPアドレスの払い出しを要求する

DHCPクライアントは、DHCPサーバーが提案した、IPアドレスの払い出しを要求します。

4.DHCPサーバーは払い出しを承認し、DHCPクライアントにIPアドレスを払い出す

DHCPサーバーは払い出し要求を受け取って承認した後、DHCPクライアントにIPアドレスを払い出します。これによりIPアドレス割り当てが完了します。

DHCPを利用するために準備すべきこと

DHCPはIPアドレス割り当ての利便性を高めるために必要な機能といえます。利用を進めるためには、まず次の二つの準備が必要です。

DHCPサーバーの構築

DHCPを利用するためには、DHCPサーバーの構築が必要になります。小規模な組織の場合は、ルーターのDHCP機能を利用することがありますが、大規模なオフィスなどでは、専用のDHCPサーバーやネットワーク機器を設置するケースが多くあります。

DHCPクライアントの設定

DHCPクライアントの設定を行います。例えば、PCにIPアドレスを割り振る際には、Windowsに備わるDHCPクライアントの設定を行います。ネットワークの設定で「IPアドレスを自動的に取得する」「DNSサーバーのアドレスを自動的に取得する」といった機能を利用します。

Webサーバーとして動作するためのプログラムの作成

Studuino:bitをアクセスポイントとして起動できたので、続いてWebサーバーとして動かしてみます。まずは下の図で全体の処理の流れを確認しておきましょう。

【 Webサーバーとして動かすときの全体の処理の流れ 】
  1. Studuino:bitはソケットを開き、クライアントからのアクセスを受け入れられるようにします。
  2. クライアント側の端末ではWebブラウザを開き、URLを指定してStuduino:bitへHTTPリクエストを送ります。
  3. Studuino:bitは受け取ったリクエストに応じて、内部に保管しているHTMLデータをレスポンスとしてクライアントへ送信します。
  4. クライアントはWebブラウザの画面上で、受け取ったHTMLデータを表示します。

サンプルで表示するHTMLデータとして、以下のページを準備しています。このページのデータをStuduino:bit内に保管しておきます。

【 Studuino:bitに保管するサンプルページ 】

3. 1 Webサーバーとして必要な機能の確認

実際のWebサーバーには、リクエストに応じてアプリケーションや画像、動画を送信したり、ユーザーの認証を行ったりするために様々な機能を備えています。それを一から構築するとなるとプログラムが複雑になってしまいますので、ここでは以下の機能だけを持つシンプルなWebサーバーとしてStuduino:bitを動かしてみましょう。

【 Studuino:bitに持たせる機能 】
  1. GETメソッドで送信されたリクエストを受け付けて、URLで指定されたファイルのテキストデータを返す。
  2. GET以外のメソッドでリクエストが送信された場合、非対応であることを表すステータスコード「405」を返す。
  3. リクエストされたファイルがサーバー内に存在しない場合は、発見に失敗したことを表すステータスコード「404」を返す。

3. 2 プログラムの作成

それでは、前のチャプターの続きからプログラムを作成します。以下の順番でコードを書いていきましょう。

  1. アドレス情報を設定してソケットを開く
  2. クライアントからの接続を受け入れる
  3. クライアントのリクエストを受信する
  4. クライアントへ送信するレスポンスを作成する
  5. クライアントへレスポンスを送る

■ アドレス情報を設定してソケットを開く

Webサーバーとして動作する処理をlaunch_webserver()関数としてまとめていきます。まずは先頭で、引数として受け取ったWLANクラスのオブジェクトのifconfig()を実行して、ソケットを開くために必要なIPアドレスを取得します。

追加【13~15行目】

ソケットを使うため、usocketモジュールをインポートします。このモジュールのgetaddrinfo()メソッドを実行して、ソケット用のアドレス情報を取得します。そして、socket()メソッドでソケットオブジェクトを作成し、bind()メソッドで取得したアドレス情報をひもづけます。最後に、listen()メソッドで接続を受け付けられるようにします。このメソッドの引数に指定した数だけ接続を受け付けることができますが、ここでは自分以外が接続することはないため「1」を設定しておきましょう。

追加【2行目、20~23行目】

■ クライアントからの接続を受け入れる

次はクライアントからの接続を受け入れるためのコードを書きます。

作成したソケットオブジェクトのaccept()メソッドを実行して、クライアントの接続を受け入れます。このメソッドは接続を受け入れると、クライアントとデータを送受信するための新たなソケットオブジェクトとクライアントのIPアドレスのタプルを返します。そして、データの送受信を終えたあとは、close()メソッドを実行して接続を解除します。受け入れは繰り返し行うため、このコードはwhile True:の無限ループで囲みます。

追加【23~27行目】

ここまでの動作を確認します。プログラムの最後の行に、作成した関数を実行するコードを追加しましょう。

追加【30行目】

一度Studuino:bitのリセットボタンを押してから、プログラムを実行します。

(実行結果の例)

ターミナルに表示された数字の並びがStuduino:bitのIPアドレスになります。

続けて、上で説明した順番でスマートフォンやタブレット端末をStuduino:bitのアクセスポイントへ接続して、ブラウザを立ち上げましょう。

立ち上げたブラウザのURLの入力欄に、以下のURLを指定します。

http://192.168.4.1

これでHTTPで「192.168.4.1」のIPアドレスをもつサーバーにリクエストを送信することができます。ブラウザから上記のURLにアクセスすると、ターミナルに次のようなメッセージが表示され、ソケットの接続に成功したことが分かります。

(実行結果の例)

ブラウザから連続して数回接続が試みられていますが、これはサーバーであるStuduino:bitからのレスポンスがなかったため、ブラウザが同じリクエストを自動的に再送した結果です。

■ クライアントのリクエストを受信する

ソケットの接続が確認できたので、次はソケットを通してクライアントから送られてきたリクエストデータを受信します。

データの受信には、ソケットオブジェクトのread()メソッドやrecv()メソッドを使います。

【 データを受信するためのソケットオブジェクトのメソッド 】
メソッド名説明
recv(bufsize)ソケットからbufsizeを最大量として、データを受信します。返り値は受信したデータを表すバイト列オブジェクトです。
recvfrom(bufsize)recv()メソッドと同様に、ソケットからbufsizeを最大量として、データを受信します。戻り値は受信したデータを表すバイト列オブジェクトと接続先のIPアドレスのタプルです。
read(size)ソケットからsizeで指定されたバイト数だけデータを読み込みます。戻り値は読み込んだデータを表すバイト列オブジェクトです。sizeが指定されなかった場合は、ソケットから終端までデータを読み込みます。
readinto(buf,[,nbytes])ソケットから読み込んだデータのバイト列をbufに格納します。nbyteが指定された場合は、最大でそのバイト数だけデータを読み込みます。
readline()ソケットから改行文字で終わる1行を読み込みます。戻り値は読み込んだ行を表すバイト列オブジェクトです。

ここではrecv()メソッドを使って、データを100バイトずつソケットから受信してみましょう。

ソケットからリクエストデータを受信する処理をget_request()関数としてまとめます。この関数では引数として接続が確立されたソケットオブジェクトを受け取ります。

また、今回のケースではrecv()メソッドを実行すると、データが受信できるまで次の処理の実行を待機します。そのため、もしもデータの受信途中に接続が切れてしまった場合は、そこで待機状態がずっと続くことになってしまいます。

これでは、次のクライアントからの接続を受け入れることができなくなってしまうため、settiomeout()メソッドを使って一定時間が経過すると自動で処理が中断されるようにします。ここではその時間を3秒に設定しておきましょう。

制限時間を超えた場合は、例外としてOSErrorが返されます。この例外を捕捉したときは、その時点で関数の処理を中断するようにします。

ここまでのことを踏まえて、以下のようにコードを書きましょう。

追加【29行目~42行目】

実際にプログラムを実行して、クライアントからのリクエストが受信できているかどうかを確認します。launch_webserver()関数内に作成したget_request()関数を実行する以下のコードを追加しましょう。

追加【26行目~27行目】

一度Studuino:bitのリセットボタンを押してから、プログラムを実行します。先ほどと同じ手順でStuduino:bitのアクセスポイントに接続し、ブラウザでURLに「http://192.168.4.1」を指定して、リクエストを送りましょう。Studuino:bitがリクエストを受信すると、以下のようなメッセージがターミナルに表示されます。

(実行結果の例)

以前のテーマで、HTTPのリクエストは次のような構造になっていることを説明しました。

つまり、実行例の1行目のGET / HTTP/1.1はリクエスト行です。リクエスト行には「メソッド名」「パス名またはURL」「HTTPのバージョン」の3つの情報が半角スペース区切りで示されています。

2行目以下はヘッダです。実行例の各ヘッダの意味は次の通りです。

ヘッダ意味
Hostリクエスト送信先のサーバーのホスト名とポート番号を指定します。ポート番号が省略されている場合は要求したサービスで既定のポート番号(HTTPの場合は80、HTTPSの場合は443)とみなされます。
Upgrade-Insecure-RequestsHTTPで指定したURLでもセキュリティで保護されたHTTPSで代替可能な場合は、クライアント側でもHTTPSでURLを指定したものとして正常に処理できることをサーバーに伝えます。
Acceptクライアントが受け入れられるコンテンツの種類をMIMEタイプでサーバーに伝えます。例えば、text/htmlはHTMLファイルを示しています。
User-Agentクライアントが使用しているブラウザの種類やバージョンなどの情報をサーバーに伝えます。
Accept-Languageクライアント側が受信可能な言語をサーバーに伝えます。
Accept-Encodingクライアント側でデコードが可能なエンコーディングの種類をサーバーに伝えます。
Connection現在のトランザクション(1回のリクエストの送信からレスポンスの受信までの処理)が完了したあとも接続を開いたままにするかどうかを制御します。keep-aliveは接続を維持したいという意思を表します。

GETメソッドの場合、リクエストにボディはありません。POSTメソッドやPUTメソッドではボディがあります。

Webサーバーはこれらの情報を受けて、クライアントへ送信するレスポンスのデータを用意します。

■ クライアントへ送信するレスポンスを作成する

それでは、クライアントへ送信するレスポンスのデータを作成するコードを書いていきます。ここではサンプルとして、以下のシンプルなWebページのデータを送信できるようにしてみましょう。

まずは、Studuino:bitに以下のソースコードをもつHTMLファイルを保存します。Muエディタで「新規」を選択し、以下のコードを複製して貼り付けましょう。

※ 以下のソースコードはHTMLとCSS、JavaScriptの3種類の言語で書かれています。これらの言語の学習は本講座の対象ではありませんので、説明は割愛します。

貼り付けたソースコードを保存します。Muエディタのメニューから「保存」を選択しましょう。表示されたウィンドウで「index.html」と名前を入力し、ファイルの種類で「Ohter(*.*)」を選択します。ファイルの種類で「Python(*.py)」を選択すると、Pythonのプログラムとして保存されてしまいますので注意しましょう。

保存先として「mu_code」を選択できたら「保存(S)」ボタンをクリックして保存しましょう。

※ htmlファイルの拡張子は「.html」です。

続けて保存したファイルをStuduino:bitに転送します。Muエディタのメニューから「ファイル」を選択して「index.html」を転送しましょう。

クライアントからこのHTMLファイルを要求するときは、以下のURLを指定します。

http://192.168.4.1/index.html

では、上記のURLでリクエストが送られてきたときに、このファイルのデータを含むレスポンスを作成するコードを書いていきます。このコードはmake_response()関数としてまとめましょう。

上記の関数は引数として、リクエストのデータを受け取ります。このリクエストのデータか、最初にメソッド名を確認します。

メソッド名は1行目のリクエスト行に含まれています。リクエスト行は情報が半角スペースで区切られているため、先頭から最初に半角スペースが現れる位置のひとつ手前までの文字列を取り出すことでメソッド名が得られます。

この処理を行うために、文字列オブジェクトのスライス操作とfind()メソッドを利用します。find()メソッドは引数に指定した文字列を検索し、最初にヒットした位置を返します。そのため、半角スペースを指定して、_str[:_str.find(" ")]とすることで、先頭から最初に半角スペースが現れた位置の手前までを取り出すことができます。

追加【47・48行目】

このWebサーバーでは、GETメソッドで送られたリクエストだけを受け入れます。

※ 実際は、GETの他にHEADもWebサーバーが必ず受け入れなければならないメソッドとして定められています。

それ以外のメソッドでリクエストが送られた場合は、対応していないことを示すステータスコード「405 Method Not Allowed」を返します。

レスポンスは上記のステータスコードを含む、ステータス行に加えて、レスポンスヘッダとレスポンスボディで構成されています。


ここでは、レスポンスのヘッダに以下の情報を含めます。

ヘッダ説明
Content-Encodingコンテンツを圧縮して転送するときのエンコーディング方式を伝えます。
Content-Lengthボディに格納したコンテンツの長さをバイト単位で伝えます。
Content-TypeMIMEタイプで表したコンテンツの種類と文字エンコーディング方式を伝えます。
Serverサーバーで使用されているソフトウェアの情報を伝えます。
Content-Encoding:identity
Content-Length: 実際のボディの長さ
Content-Type: text/plain; charset=UTF-8
Server: MicroPython

Content-Encodingの値「identity」は、コンテンツを圧縮していないことを表します。Content-Lengthの値はプログラム内でバイト列化したボディの長さを調べて設定します。また、Content-Typeの値「text/plain」はコンテンツがテキスト(普通の文章)であることを表します。そして、Studuino:bitはMicroPythonで制御しているので、Serverの値には「MicroPython」を設定します。

レスポンスのボディには、リクエストで指定されたメソッドを受け入れできないことを伝えるためのメッセージを入れておきましょう。

以上のことを踏まえて以下のようにコードを書きます。ここでは、statusbodyheaderに分けて変数に格納しておき、最後に3つを結合します。また、ヘッダは辞書型のオブジェクトとして定義しておきましょう。

追加【50~59行目】

続けて、GETメソッドのリクエストを受信した場合のコードを書いていきます。GETはリクエスト行で指定されているURL(またはパス名)のファイルの送信を要求するメソッドのため、サーバー内にそのファイルが存在している場合は、それを送信します。もしファイルが存在しなかった場合は、「404 Not Found」のステータスコードを送信します。

まずは、リクエストのデータからURLを取り出すコードを以下のように書きます。手順は最初の改行までをリクエスト行として取り出して、さらにfind()メソッドとrfind()メソッドで1番目の半角スペースと2番目の半角スペースの位置を調べて取り出すというものです。rfind()メソッドは、find()メソッドと同じ検索を末尾から行います。

変更・追加【60~67行目】

また、URLは、「http://~」から書かれた絶対パスと、ホスト名以降のパスのみが書かれた相対パスのいずれかで示されています。ここでは、相対パスで書かれたURLを取得したいので、絶対パスの場合を想定して、http://192.168.4.1の文字列を削除しておきます。

次にStuduino:bit内にこのパス名で示されたファイルが存在しているかどうかを調べます。これには、例外処理を使います。try文の中でパスを指定してファイルを開くコードを実行すると、ファイルが存在しなかった場合は、例外としてOSErrorが発生します。これをexcept文で捕捉します。

変更・追加【60~67行目】

ファイルが存在しなかった場合は、「404 Not Found」のステータスコードと、存在しなかったこを示すメッセージを送信します。ファイルが存在した場合は、ファイルから読み込んだテキストをボディとして格納して、成功を表すステータスコード「200 OK」を送信します。それぞれボディに格納するコンテンツの種類が異なる点に注意して、以下のようにコードを書きましょう。

追加【66~83行目】

これで、「GET以外でリクエストが送られたとき」「GETでリクエストされたファイルが存在しなかったとき」「GETでリクエストされたファイルが存在したとき」の3つの場合におけるレスポンスの「ステータス行」「ヘッダ」「ボディ」が用意できたので、最後に結合します。ヘッダとボディの間には空白が1行必要な点に注意してください。データはバイト列化して送る必要があるため、encode()メソッドを使いutf-8でエンコードしましょう。

追加【85~90行目】

■ クライアントへレスポンスを送信する

いよいよレスポンスをクライアントへ送信します。クライアントへデータを送信するときは、ソケットオブジェクトのsend()メソッドやwrite()メソッドを使います。

【 データを送信するためのソケットオブジェクトのメソッド 】
メソッド名説明
send(bytes)指定されたバイト列オブジェクト(bytes)のデータをソケットに送信します。このメソッドは、戻り値として送信に成功したデータのバイト数を返します。ただし、指定されたデータが大きい場合、全てのデータが送信できず、戻り値が実際のデータの長さよりも短くなる場合があります。
sendall(bytes)指定されたバイト列オブジェクト(bytes)のすべてのデータをソケットに送信します。このメソッドは、send()メソッドと違い、データ量が大きい場合は、分割して連続して送ることによって、すべてのデータを送信しようとします。
write(bytes)sendall()と同様に、指定されたバイト列オブジェクト(bytes)をすべてソケットに送信しようとしますが、必ずしもすべてのデータが送信できるとは限りません。返り値はソケットに送信できたデータのバイト数です。

細かな違いはありますが、どのメソッドを使ってもデータを送ることができます。ここでは、sendall()メソッドを使ってデータを送信してみましょう。launch_webserverに以下のコードを追加します。

追加【31~34行目】

では、作成したプログラムの動作を確認しましょう。

一度Studuino:bitのリセットボタンを押してから、プログラムを実行します。Studuino:bitのアクセスポイントに接続し、ブラウザのURL欄に「http://192.168.4.1/index.html」を入力して、リクエストを送りましょう。ブラウザ上に以下のページが表示されれば成功です。

また、誤ったURL(例えばhttp://192.168.4.1/sample.htmlなど)でリクエストを送ったときには、以下のようなページが表示されることも確認しておきましょう。

現在の内蔵センサの値を送信するプログラムの作成

前のチャプターでは、クライアントから要求されたファイルを送信する機能をもったWebサーバーのプログラムを作成しました。また、送信するファイルはあらかじめ別で作成してサーバー内に保管していたものでした。

実際のWebサーバーには、要求されたファイルを送信する以外にも、要求されたプログラムを実行して、その結果として生成されたファイルを送信する仕組みがあります。

ここでは、Studuino:bitに内蔵された「ボタンA」「ボタンB」「光センサ」「温度センサ」の4つのセンサの現在の状態を取得して、その結果をレスポンスとして送信できるようにプログラムを改造してみましょう。

4. 1 内蔵センサの値を取得するプログラムの作成

Webサーバーとして起動するプログラムとは別に、4つの内蔵センサの値を取得して、一覧にしたテキストとMIMEタイプを返す関数を定義したプログラムを作成します。

Muエディタのメニューから「新規」を選択して、以下のコードを書きましょう。

【 サンプルコード 4-1-1 】

このプログラムを「monitor(.py)」と名前を付けて保存します。Muエディタのメニューから「ファイル」を選択して、保存したプログラムをStuduino:bitに転送しましょう。

このプログラムファイルをあらかじめWebサーバーのプログラム内でインポートしておき、クライアントから実行が要求されたときは、main()関数を実行します。

4. 2 Webサーバーのプログラムの変更

プログラムの実行が要求された場合に対応するコードを追加していきます。

先頭では、Studuino:bitに転送した「monitor(.py)」をモジュールとしてインポートします。また、ファイルのURLとインポートしたモジュール名をひもづけした辞書を定義しておきましょう。

追加【3行目、8~10行目】

次に、make_response()関数を変更します。

ファイルが存在する場合は、ファイルの種類によって実行する処理を分けます。ファイルの拡張子が「.py」の場合(Pythonのプログラムの場合)は、関連するモジュール名を上で定義した辞書から取得し、eval()関数を使って、そのmain()関数を実行します。そして、その結果をレスポンスのボディとします。

ファイルの拡張子が「.html」の場合(HTMLファイルの場合)は、読み込んだテキストをレスポンスのボディとします。

変更・追加【83~97行目】

これで変更が完了しました。プログラムの動作を確認しましょう。

一度Studuino:bitのリセットボタンを押してから、プログラムを実行します。Studuino:bitのアクセスポイントに接続し、ブラウザのURL欄に「http://192.168.4.1/monitor.py」を入力して、リクエストを送りましょう。ブラウザ上に4つのセンサの値が表示されたら成功です。

完成したプログラムは名前を付けて保存しておきましょう。次回以降のテーマでもこのプログラムを使います。

【 プログラムの完成例(サンプルコード 4-2-1) 】

課題:ページの見た目を整えよう

前のチャプターでは、取得したセンサの値をそのままテキストとしてクライアントへ送信していましたが、HTMLファイルにまとめて送信すると、以下のように表に整理された形でクライアントがセンサの値を確認できるようになります。

このチャプターでは、上のページを表示するためのベースとなるHTMLファイルのテキストから、一部を取得したセンサの値に変更して、そのファイルをクライアントへ送信するようにコードを改造する課題に取り組みましょう。

5. 1 ベースとするHTMLファイルの確認

ベースとなるHTMLファイルのソースコードは以下になります。Muエディタのメニューで「新規」を選択して、このソースコードを貼り付けましょう。保存するときは、ファイルの種類で「Other(*.*)」を選び、ファイル名を「base.html」としましょう。

このファイルをそのままブラウザで開くと次のような画面が表示されます。

この中の{val_buttonA}{val_buttonB}の部分をそれぞれ取得したセンサ値に書き換えます。HTMLのソースコードを確認すると、この部分は70行目~73行目に書かれています。HTMLは<>を使ったタグで文書の構造を表すようになっており、65行目の<table>から77行目の</table>のタグで囲まれたところが、画面上に表示された表(テーブル)の構造を定義しています。

それでは準備として、保存したHTMLファイル(base.html)を転送しておきましょう。

5. 2 プログラムの改造

上で保存したHTMLファイルの一部を書き換えて送信するようにプログラムを変更します。変更するプログラムファイルは「monitor.py」です。

■ monitor.pyの追加・変更点

前のチャプターで作成した「monitor.py」へ以下の3つの処理を追加・変更します。

  1. base.htmlを開いて、テキストを読み込む。
  2. 文字列のreplace()メソッドを使って、読み込んだテキストの中から{val_buttonA}{val_buttonB}{val_lightsensor}{val_temp}の4つの文字列を、取得したそれぞれのセンサ値に置き換える。
  3. 2の置き換えたテキストとMIMEタイプとして「html/text」を呼び出し元に返す。

以上をヒントにして自分でコードを書きましょう。手順が分からない場合は、以下の例を参考にしてください。

■ プログラムの改造例

PCに保存したmonitor.pyを開いて、以下の箇所を変更します。

追加・変更【5~7行目、14~17行目、19行目】

変更したプログラムを保存して、Studuino:bitに転送し、上書き保存します。

動作を確認するときは、一度Studuino:bitのリセットボタンを押してから、Webサーバーのプログラムを実行します。Studuino:bitのアクセスポイントに接続し、ブラウザのURL欄に「http://192.168.4.1/monitor.py」を入力して、リクエストを送りましょう。

表示された画面上で「Update」と表示されたボタンをタップすると、同じURLに再度リクエストを送ることができ、センサの値が更新されます。

※ ボタンが見切れる場合は、端末の画面を縦にして表示してください。

おわりに

6. 1 このレッスンのまとめ

このレッスンでは、Studuino:bitをWi-Fiのアクセスポイントとして起動し、さらにWebサーバーとして動作させるためのプログラムの作り方を新たに学習しました。

実際にStuduino:bit内にHTMLファイルを保管し、アクセスポイントに接続した手元のスマートフォンやタブレットからリクエストを送信すると、そのページを開くことができました。また、クライアントからプログラムファイルの実行を要求できることも確認し、Studuino:bitに内蔵された各種センサの値をブラウザの画面に表示する課題に取り組みました。

テーマ11ではクライアントとしてStuduino:bitをネットワークに接続して、インターネットから情報を取得しましたが、このレッスンで取り組んだようにStuduino:bitをWebサーバーとすることで、反対にクライアントの要求の応じてセンサから得たデータを送ったり、モーターを制御したりすることができます。本格的にIoTを実践するときには、どちらの知識も必要になってきますので、残りのテーマの学習を通してさらに理解を深めていきましょう。

6. 2 次回のレッスンについて

次のレッスンでは、同じくStuduino:bitをWebサーバーとして動かし、今度はブラウザの画面に表示されたボタンやスライダーを操作することで、Studuino:bitを遠隔で制御できるプログラムを作成します。

TOP