HTTPプロトコルで構成したWEBサーバーを搭載したマイコンシステム【STM32Nucleo】
マイコンにTCPサーバーが搭載されていればHTTPプロトコルで構成したWEBサーバーに発展させることは容易です。WEBサーバーを搭載したシステムの利点は、ブラウザからアクセスできるためにPC、スマホ等どの端末からも専用アプリをインストールしなくても通信でき、遠隔操作管理等のIoT化を容易に実現できることです。
今回はNUCLEO-F103RBのようなメモリの小さいマイコンに最低限の機能をもったWEBサーバーを搭載してみます。
WiFiモジュールESP32のときもWEBサーバーを扱ったのですが、メーカー提供のHTTPドライバフォーマットに従っただけのため、プロトコル内容は意識せずともブラウザからアクセスできました。今回は一般的なHTTPプロトコルの知識をもってより自由度の高い簡易的なサーバーを構築したいと思います。
目次
HTTPプロトコルとは
これに関しては解説記事が世に溢れているため、詳細解説はここではあえてしません。マイコンでWEBサーバーを構成するにあたってつまずきやすいポイントにしぼって最低限必要な知識の要点をまとめるように努めています。
HTTPはTCPの上位にあるプロトコルでHTTPリクエストとHTTPレスポンスと呼ばれるHTTPメッセージでやり取りしているのですが、目に見えない水面下ではTCPによるデータ通信を行っています。
HTTPはブラウザなどのクライアントからリクエストをうけてサーバーがレスポンスを返すプロトコルです。すなわち、サーバーはデータが定期的に更新されてもクライアントからのリクエストがない限りデータを返すことはありません。
マイコンに搭載するWEBサーバー
通常、WEBサーバーはファイルシステムをもっているコンピュータシステムにHTMLで記述したページ、CGIプログラム、さらに画像ファイル等を保存しておき、クライアントからの指示に従ってページを渡したり、プログラムを実行したりブラウザへ応答するものです。
対して、ファイルシステムを持たないマイコンの場合はサーバーは個々のファイルが作成できないために、かわりにすべてのデータを静的な文字列にコード化(ハードコーディング)して配列に格納して文字列の送信でブラウザに表示させます。
ファイルシステムがないサーバーは画像ファイルが扱えないために、見た目のシンプルな簡易的なものに限られますが、最低限の機能でもブラウザからマイコンにアクセスできてリモート管理等できることは大きな意義があります。
マイコンはブラウザへHTMLファイルをハードコーディングして配列に格納したものを送信します。HTMLファイルは基本的には静的なファイルなのですが、動的な数値などを結合したり、スクリプトファイルを埋め込んで実行することもできます。
HTTPフォーマット
HTTPはWEB専用でブラウザとサーバの間でやりとりするプロトコルです。クライアントであるブラウザがリクエストメッセージ(要求メッセージ)をだしてWEBサーバーはレスポンスメッセージ(応答メッセージ)を返します。
HTTPプロトコルはテキストで記述したヘッダーを送受信したいデータに加えたフォーマットになっています。
① HTTPリクエスト
HTTPリクエストの一行目はリクエストラインでメソッドを含んだコマンドラインのような情報を含んでいます。
メッセージヘッダが続いてブラウザの種類やデータのタイプ等情報を伝えています。
空行を入れることでヘッダの終了を伝え、続いてメッセージボディに伝えたいデータを格納します。空の場合もあります。
下のコードはアクセスしたいWEBサーバーのURLをブラウザから入力したときのリクエストメッセージです。一行目はリクエストラインでメソッドGETから始まりアクセスしたいURL等が続きます。
ついでメッセージヘッダでブラウザの情報を伝えています。このリクエストにはデータはありませんのでボディーは空です。
上記コードはGETメソッドでURLが192.168.3.100:50000へのアクセスリクエストです。 URLにはアクセスしたいファイルのパス名まで入れることもありますが、省略したときはトップページのHTMLを要求していることになります。
HTTPプロトコルの規定ポート番号80以外の場合にはポート番号を明記します。
② HTTPレスポンス
HTTPレスポンスの一行目はステータスラインでWEBサーバーの処理の結果を伝えます。
メッセージヘッダが続いてサーバーのソフトウェア情報や返信データタイプ、圧縮方法、渡すデータの長さなどを伝えます。
空行を経てメッセージボディにHTMLなどのデータを格納します。
HTTPレスポンスにはWEBサーバーの情報を記述します。ヘッダ項目でHTMLで記述したページを送りたい場合はContent-Typeにtext/htmlとします。次のContent-Lengthはメッセージボディのデータ長を指定します。これらの設定はブラウザに内容を正しく表示する上で重要です。
メッセージボディはHTMLで記述したページ等の内容です。上記のコードはマイコンからブラウザへHTMLをコード化(ハードコーディング)した文字列を送信した内容でファイルシステムでのHTMLファイルを送るのと同じものになります。
ブラウザからの入力フォームで送信ボタンにGETメソッドを指定(method="get")しています。
HTTPメソッド
HTTPメソッドとはクライアントがWEBサーバーに要求する処理のことで、数種類ありますがブラウザとマイコンとのやり取りでは主にGETメソッドとPOSTメソッドの2種類を扱います。
ブラウザがサーバーからHTMLファイルなどを取得する場合にはGETメソッドを、ブラウザからサーバーにデータを送信する場合はPOSTメソッドを使います。GETメソッドはPOSTメソッドのようにデータ送信にも使えますが、その場合はURLの後ろにデータを付けて送信します。
① GETメソッドによるWEBページアクセス
標準的なGETメソッドの用法です。ブラウザ(クライアント)はWEBページのURLを入力するとまずはじめにサーバーへTCP接続を要求します。
サーバー側がクライアント側からのTCP接続要求を受け入れて接続(コネクション)が確立するとHTTPプロトコルでのやり取りがはじまります。
ブラウザはGETメソッドリクエストラインから始まるリクエストメッセージをサーバーに送信します。
サーバーはGETリクエストを受けて次のレスポンスを返します。
HTTP/1.1 200 OK
ブラウザはGETリクエストの内容によりHTMLページを開いたり、指定されたファイルにアクセスします。
② GETメソッドによるデータ送信
GETメソッドを使ったデータ送信フォーマットです。ブラウザのアドレス欄にURL末尾に「?」をつけて「データ=xxx」を送ります。
サーバーは下記のようなフォーマットでリクエストメッセージを受信データとして取得できますのでメソッド「GET」とデータ「ABC」を処理して抽出すればよいだけです。
GETメソッドによるデータの送信はとても簡単な方法ですが、URL欄に履歴が残るために機密データを送信するには問題があります。
③ POSTメソッドによるデータ送信
基本的にデータの送信にはPOSTメソッドを使います。GETメソッドとは違いブラウザの履歴は残りません。
サーバーは下記のようなフォーマットでリクエストメッセージを受信データとして取得できます。データはリクエストヘッダが終了した空行のあとのボディにあります。
リクエストすべてを受信してからのメソッド「POST」とボディ内のデータ「ABC」を処理して抽出することになるため、「GET」のときに比べて受信データ量は大きなものになります。
マイコンへの実装
マイコンシステムにWEBサーバーを実装するときに重要なポイントをまとめました。
WEBサーバー搭載プログラム
WEBサーバーを搭載してもプログラムの基本的な流れはTCPサーバーの時とあまり変わりません。送受信データにHTTPプロトコル特有の多少の尾ひれがつくのでその処理が追加になります。
STM32マイコンにWEBサーバーを搭載したプログラム概要です。HTTPプロトコル特有の内容はモジュールhttp_server.cに記述しておき、ヘッダファイル http_server.h を読み込んでおきます。
ここではサーバー内でのデータをブラウザで表示するデモ用プログラムとしてRTOSのタスクprvTask_monitorを一つ追加してその中でデータ値を1秒間に1カウントアップしています。
マイコンにWEBサーバーを実装するうえで重要なポイントはいくつかあるのですがとりわけ重要なものがメモリの扱いです。TCPサーバーとの違いは扱う文字列が長いことです。FreeRTOSを使用しているときは長い文字列を扱うタスクのスタック容量を大きめ(384ワード程度:最低1kバイト)に設定しておくことが重要です。
WEBサーバーモジュール
下のWEBサーバーモジュールは今回のデモ用WEBサーバーの中核となる部分です。ブラウザでURLを入力して起動するトップページは配列top_document[]の内容です。GETメソッドリクエストにデータが含まれない場合のページです。
送信した文字列があらかじめ設定した文字列"ABC"と一致したときと不一致のときのメッセージを表示する別ページは配列top_document2[]の内容です。GETメソッドリクエストにデータが含まれる場合のページです。
HTMLファイルをコード化して配列に格納するにあたって最も重要なポイントは変化のない静的なコードと内容により異なるコード長データ"len_top_document"など動的なコードをsprintf関数で結合しているところです。これを利用すると静的なファイルであるはずのHTMLが動的な要素を含んだページになります。
ブラウザからのリクエストに対するレスポンスはここでは簡略化のため"HTTP/ 1.1 200 OK"に固定しています。厳密にはHTTPプロトコルではブラウザからのリクエストバージョンに整合したり、ステータスコードを状態に応じて割り当てたものを返します。
日本語の表示させるとブラウザで文字化けする場合はサーバー側で対処しておきます。上のコードでは文字コードをUTF-8からにUTF-8Nにしただけで解決できたのですが対処療法的なのでより根本的な解決方法を適用してください。
受信文字処理
ブラウザからのリクエストメッセージにはGETやPOSTなどのメッセージおよびデータが含まれていますので、それらをまず正確に抽出する処理が必要です。これらはタスクprvTask_TCP内で処理を行っています。
今回のデモプログラムではデータ送信にGETメソッドを使用しているために「GET」をコマンドとして受信した場合にリクエストがデータなしかデータ付きかで処理を分岐しています。
Nucleo-F103RBはメモリがあまり大きくないので、FreeRTOSにWEBサーバーを搭載する場合はFreeRTOSの設定ファイルFreeRTOSConfig.hのメモリ割り当てに関する部分は見直したほうがよいです。特にヒープサイズを指定するconfigTOTAL_HEAP_SIZEは使用するタスク数が少なければ小さく設定しても大丈夫です。動作する範囲で調整してください。
ブラウザとマイコンとの通信
Nucleo-F103RBにWEBサーバーを搭載したところで実際にブラウザにURLのIPアドレスとポートを入力してアクセスしてみます。
URLを入力するとサーバーとのコネクションが確立して以下のページに切替わります。配列top_document[]にコード化した内容です。 左下の数値はマイコン内で1秒毎にカウントアップしているデータを表示したものです。現在値を確認するにはブラウザを手動で更新します。
以下のフォーム画面で文字を入力してGETメソッドでサーバーに送ります。文字"ABC"を入力して送信ボタン(Send)を押します。このボタンはGETメソッドに設定しています。
入力文字を履歴にも残さない伏せ字にするにはHTMLの入力タグを<input type="text">から<input type="password">に変更するだけです。これを利用すると簡易にパスワード要求画面が作成できます。ただし、送信メソッドはPOSTにする必要がありますが。
送信ボタンを押すとブラウザのアドレス欄にURL/?cmd=ABCが自動的に入り、ページが配列top_document2[]に格納したHTMLページに切替わります。入力した文字が“ABC”と一致すると下記のメッセージが表示されます。
今度は"abc"を入力して送信ボタンを押すとブラウザのアドレス欄にURL/?cmd=abcが自動的に入り、ページが切替わります。入力した文字が“ABC”と不一致であると下記のメッセージが表示されます。
以上、ブラウザからマイコンにアクセスして文字列を送信する様子とマイコンからのデータを表示することを確認しました。このデモプログラムは、機能は単純ですがこれらを自由自在に扱いこなせるとマイコンをブラウザからリモート管理することができる重要な要素を納めていますので是非実践してみてください。C言語で文字列を扱うトレーニングにもなります。
サーバーからブラウザへ送られる数値は手動で更新していますが、HTMLのmeta refreshタグで自動更新することもできます。HTTPプロトコルは基本的にクライアントからリクエストしなければデータは更新できないのですが、リアルタイムで表示する方法として、server sent events(SSE)や別のプロトコルであるWebSocketなどがあります。ファイルシステムを持たないマイコン搭載のWEBサーバーには敷居が高いので当面は自動更新で十分かと思います。
HTTPプロトコルを扱うためにはクライアントとサーバー間で実際にやりとりされている生のデータ(パケット)を確認しながら行うことが重要です。
"Fiddler"というフリーソフトはツールの一つでブラウザとマイコン間でやり取りするパケットをモニターできますのでプログラム開発やトラブルシュートに役に立ちます。
ここまでくるとマイコンにファイルシステムを搭載してWEBサーバーでプログラムを搭載した高度なものを実現したくなります。ファイルシステムさえあれば一般的なWEBサーバーの方法が採用できるのでかえって楽に高度なことができるので機会があれば挑戦したいと思います。