さまざまな種類のスマートソケット、電球、およびその他の同様のデバイスが私たちの生活に登場するにつれて、マイクロコントローラー上のWebサイトの必要性は否定できなくなりました。そして、lwIPプロジェクト(およびその弟のuIP)のおかげで、そのような機能を持っている人を驚かすことはありません。しかし、lwIPは、設計、機能、および使いやすさと開発の点でリソースを最小限に抑えることを目的としているため、このようなサイトは、私たちが使用しているサイトよりもはるかに遅れています。組み込みシステムの場合でも、たとえば、最も安価なルーターの管理サイトと比較してください。この記事では、いくつかのスマートデバイス用にLinuxでサイトを開発し、それをマイクロコントローラーで実行しようとします。
マイクロコントローラーで実行するには、Emboxを使用します 。このRTOSには、CGI対応のHTTPサーバーが含まれています。Linuxでは、Pythonに組み込まれているHTTPサーバーをHTTPサーバーとして使用します。
python3 -m http.server -d <site folder>
静的サイト
1つ以上のページで構成される単純な静的サイトから始めましょう。
ここではすべてが簡単です。その中にフォルダとindex.htmlを作成しましょう。ブラウザでサイトアドレスのみが指定されている場合、このファイルはデフォルトでダウンロードされます。
$ ls website/ em_big.png index.html
このサイトには、Emboxのロゴである「em_big.png」ファイルも含まれています。このファイルはhtmlに埋め込まれます。
httpサーバーを起動しましょう:
python3 -m http.server -d website/
ブラウザでlocalhost:8000に移動しましょう。
次に、静的サイトをEmboxファイルシステムに追加しましょう。これは、フォルダーをrootfs / templateフォルダーにコピーすることで実行できます(現在のテンプレートはconf / rootfsフォルダーにあります)。または、rootfsのファイルを指定するモジュールを作成します。
$ ls website/ em_big.png index.html Mybuild
Mybuildのコンテンツ。
package embox.demo module website { @InitFS source "index.html", "em_big.png", }
簡単にするために、サイトをルートフォルダー(パラメーターなしの@InitFsアノテーション)に直接配置します。
また、mods.conf構成ファイルにサイトを含め、そこにhttdサーバー自体を追加する必要があります。
include embox.cmd.net.httpd include embox.demo.website
また、システム起動時に当社のウェブサイトでサーバーを起動しましょう。これを行うには、conf /system_start.incファイルに次の行を追加します。
"service httpd /",
当然、これらの操作はすべて、ボードの構成を使用して実行する必要があります。その後、収集して実行します。ブラウザでボードのアドレスに移動します。私の場合は192.168.2.128
で、ローカルサイトと同じ写真です。
私たちはウェブ開発の専門家ではありませんが、美しいウェブサイトを作成するためにさまざまなフレームワークが使用されていると聞いています。たとえば、AngularJSがよく使用され ます。したがって、それを使用してさらに例を示します。しかし同時に、どこかでウェブデザインを強く調整した場合は、詳細に立ち入り、事前に謝罪することはありません。
jsファイルやcssファイルなど、サイトフォルダーに配置する静的コンテンツが何であれ、追加の作業なしで使用できます。
app.js(角度のあるサイト)を私たちのサイトに追加して、その中にいくつかのタブを追加しましょう。これらのタブのページをpartialsフォルダーに、画像をimages /フォルダーに、cssファイルをcss /に配置します。
$ ls website/ app.js css images index.html Mybuild partials
私たちのウェブサイトを立ち上げましょう。
同意します、サイトははるかに親しみやすく快適に見えます。そして、これはすべてブラウザ側で行われます。すでに述べたように、コンテキスト全体はまだ静的です。そして、通常のウェブサイトのようにホスト上で開発することができます。
当然、一般的なWeb開発者のすべての開発ツールを使用できます。そのため、ブラウザでコンソールを開くと、favicon.icoが見つからないというエラーメッセージが
見つかりました。これがブラウザタブに表示されるアイコンであることがわかりました。もちろん、この名前のファイルを置くこともできますが、この場所にお金をかけたくない場合もあります。メモリが少ないマイクロコントローラでも実行したいことを思い出させてください。
インターネットで検索すると、ファイルがなくても実行できることがすぐにわかりました。headhtmlセクションに行を追加するだけです。エラーは干渉しませんでしたが、サイトを少し良くすることは常に楽しいことです。そして最も重要なことは、通常の開発者ツールが提案されたアプローチに完全に適用できることを確認したことです。
動的コンテンツ
CGI
動的コンテンツに移りましょう。 Common Gateway Interface(CGI)は、 Webサーバーとコマンドラインユーティリティを相互作用させるためのインターフェイスであり、動的コンテンツの作成を可能にします。つまり、CGIを使用すると、ユーティリティの出力を使用して動的コンテンツを生成できます。
いくつかのCGIスクリプトを見てみましょう。
#!/bin/bash
echo -ne "HTTP/1.1 200 OK\r\n"
echo -ne "Content-Type: application/json\r\n"
echo -ne "Connection: Connection: close\r\n"
echo -ne "\r\n"
tm=`LC_ALL=C date +%c`
echo -ne "\"$tm\"\n\n"
最初に、httpヘッダーが標準出力に印刷され、次にページ自体のデータが印刷されます。出力はどこにでもリダイレクトできます。このスクリプトは、コンソールから簡単に実行できます。次のように表示されます。
./cgi-bin/gettime
HTTP/1.1 200 OK
Content-Type: application/json
Connection: Connection: close
"Fri Feb 5 20:58:19 2021"
また、標準出力の代わりにソケットである場合、ブラウザはこのデータを受信します。
CGIはスクリプトで実装されることが多く、cgiスクリプトでさえ言われています。しかし、これは必須ではありません。スクリプト言語では、そのようなことがより速く、より便利になります。CGIを提供するユーティリティは、任意の言語で実装できます。そのため、マイクロコントローラーに重点を置いているため、リソースの節約に努めています。Cでも同じことをしましょう。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[]) {
char buf[128];
char *pbuf;
struct timeval tv;
time_t time;
printf(
"HTTP/1.1 200 OK\r\n"
"Content-Type: application/json\r\n"
"Connection: Connection: close\r\n"
"\r\n"
);
pbuf = buf;
pbuf += sprintf(pbuf, "\"");
gettimeofday(&tv, NULL);
time = tv.tv_sec;
ctime_r(&time, pbuf);
strcat(pbuf, "\"\n\n");
printf("%s", buf);
return 0;
}
このコードをコンパイルして実行すると、スクリプトの場合とまったく同じ出力が表示されます。
app.jsに、タブの1つに対してCGIスクリプトを呼び出すハンドラーを追加しましょう。
app.controller("SystemCtrl", ['$scope', '$http', function($scope, $http) {
$scope.time = null;
$scope.update = function() {
$http.get('cgi-bin/gettime').then(function (r) {
$scope.time = r.data;
});
};
$scope.update();
}]);
組み込みのPythonサーバーを使用してLinux上で実行するための小さなニュアンス。CGIをサポートするには、起動行に--cgi引数を追加する必要があります。
python3 -m http.server --cgi -d .
動的コンテンツの自動更新
次に、動的サイトのもう1つの非常に重要なプロパティである自動コンテンツ更新を見てみましょう。その実装にはいくつかのメカニズムがあります。
- サーバーサイドインクルード(SSI)
- サーバー送信イベント(SSE)
- WebSocket
- 等
サーバーサイドインクルード(SSI)
サーバーサイドインクルード(SSI)。これは、Webページを動的に作成するための単純な言語です。通常、SSIを使用するファイルは.shtml形式です。
SSI自体にも、他の場合は制御ディレクティブがあります。しかし、私たちが見つけたほとんどのマイクロコントローラーの例では、次のように使用されています。ページ全体を定期的に再読み込みするディレクティブが.shtmlページに挿入されます。これは、たとえば次のようになります。
<meta http-equiv="refresh" content="1">
または:
<BODY onLoad="window.setTimeout("location.href='runtime.shtml'",2000)">
そして、何らかの形で、コンテンツは、たとえば、特別なハンドラーを設定することによって生成されます。
この方法の利点は、その単純さと最小限のリソース要件です。しかし一方で、これはそれがどのように見えるかの例です。
ページの更新(タブを参照)は非常に目立ちます。また、ページ全体をリロードすることは、過度に冗長なアクションのように見えます。
FreeRTOSの標準的な例が提供されています-https ://www.freertos.org/FreeRTOS-For-STM32-Connectivity-Line-With-WEB-Server-Example.html
サーバー送信イベント
サーバー送信イベント(SSE)は、クライアントとサーバー間の半二重(一方向)接続を可能にするメカニズムです。この場合、クライアントは接続を開き、サーバーはそれを使用してデータをクライアントに転送します。同時に、応答を生成してクライアントに送信してから完了することを目的とする従来のCGIスクリプトとは異なり、SSEは「連続」モードを提供します。つまり、サーバーは、サーバー自体が完了するか、クライアントが接続を閉じるまで、必要なだけのデータを送信できます。
通常のCGIスクリプトとは若干の違いがあります。まず、httpヘッダーは少し異なります。
"Content-Type: text/event-stream\r\n"
"Cache-Control: no-cache\r\n"
"Connection: keep-alive\r\n"
ご覧のとおり、接続は閉じていませんが、維持されています。つまり、継続的な接続です。ブラウザがデータをキャッシュしないようにするには、Cache-Controlno-cacheを指定する必要があります。最後に、特別なデータ型Content-Type text / event-streamが使用されることを指定する必要があります。
このデータ型は SSEの特別な形式です。
: this is a test stream data: some text data: another message data: with two lines
この場合、データは次の行にパックする必要があります。
data: { “time”: “<real date>”}
CGIスクリプトは次のようになります。
#!/bin/bash
echo -ne "HTTP/1.1 200 OK\r\n"
echo -ne "Content-Type: text/event-stream\r\n"
echo -ne "Cache-Control: no-cache\r\n"
echo -ne "Connection: keep-alive\r\n"
echo -ne "\r\n"
while true; do
tm=`LC_ALL=C date +%c`
echo -ne "data: {\"time\" : \"$tm\"}\n\n" 2>/dev/null || exit 0
sleep 1
done
スクリプトを実行した場合の出力:
$ ./cgi-bin/gettime
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
data: {"time" : "Fri Feb 5 21:48:11 2021"}
data: {"time" : "Fri Feb 5 21:48:12 2021"}
data: {"time" : "Fri Feb 5 21:48:13 2021"}
など、1秒に1回。
Cでも同じこと:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[]) {
char buf[128];
char *pbuf;
struct timeval tv;
time_t time;
printf(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/event-stream\r\n"
"Cache-Control: no-cache\r\n"
"Connection: keep-alive\r\n"
"\r\n"
);
while (1) {
pbuf = buf;
pbuf += sprintf(pbuf, "data: {\"time\" : \"");
gettimeofday(&tv, NULL);
time = tv.tv_sec;
ctime_r(&time, pbuf);
strcat(pbuf, "\"}\n\n");
if (0 > printf("%s", buf)) {
break;
}
sleep(1);
}
return 0;
}
そして最後に、SSEがあることをAngularに伝える必要があります。つまり、コントローラーのコードを変更します。
app.controller("SystemCtrl", ['$scope', '$http', function($scope, $http) {
$scope.time = null;
var eventCallbackTime = function (msg) {
$scope.$apply(function () {
$scope.time = JSON.parse(msg.data).time
});
}
var source_time = new EventSource('/cgi-bin/gettime');
source_time.addEventListener('message', eventCallbackTime);
$scope.$on('$destroy', function () {
source_time.close();
});
$scope.update = function() {
};
$scope.update();
}]);
サイトを立ち上げると、次の
ことがわかります。SSIを使用する場合とは異なり、ページが過負荷にならず、データがスムーズかつ快適に更新されていることがわかり ます。
デモ
もちろん、与えられた例は非常に単純であるため、実際のものではありません。彼らの目標は、マイクロコントローラーと他のシステムで使用されているアプローチの違いを示すことです。
実際のタスクを使って小さなデモを作成しました。LEDの制御、角速度センサー(ジャイロスコープ)からのリアルタイムデータの受信、およびシステム情報を含むタブ。
このサイトはホスト上で開発されました。LEDとセンサーからのデータをエミュレートするために小さなプラグを作るだけで済みました。センサーデータは、標準のランダムを通じて受信された単なるランダムな値です
#!/bin/bash
echo -ne "HTTP/1.1 200 OK\r\n"
echo -ne "Content-Type: text/event-stream\r\n"
echo -ne "Cache-Control: no-cache\r\n"
echo -ne "Connection: keep-alive\r\n"
echo -ne "\r\n"
while true; do
x=$((1 + $RANDOM % 15000))
y=$((1 + $RANDOM % 15000))
z=$((1 + $RANDOM % 15000))
echo -ne "data: {\"rate\" : \"x:$x y:$y z:$z\"}\n\n" 2>/dev/null || exit 0
sleep 1
done
LEDの状態をファイルに保存するだけです。
#!/bin/python3
import cgi
import sys
print("HTTP/1.1 200 OK")
print("Content-Type: text/plain")
print("Connection: close")
print()
form = cgi.FieldStorage()
cmd = form['cmd'].value
if cmd == 'serialize_states':
with open('cgi-bin/leds.txt', 'r') as f:
print('[' + f.read() + ']')
elif cmd == 'clr' or cmd == 'set':
led_nr = int(form['led'].value)
with open('cgi-bin/leds.txt', 'r+') as f:
leds = f.read().split(',')
leds[led_nr] = str(1 if cmd == 'set' else 0)
f.seek(0)
f.write(','.join(leds))
同じことがCバリアントでも簡単に実装されます。必要に応じて、リポジトリフォルダー(プロジェクト/ Webサイト)でコードを確認できます 。
もちろん、マイクロコントローラーでは、実際の周辺機器と相互作用する実装が使用されます。ただし、これらは単なるコマンドとドライバーであるため、個別にデバッグされました。したがって、サイトのマイクロコントローラへの転送自体には時間がかかりませんでした。
ホストで実行されているスクリーンショットは次のようになります。
短いビデオで、実際のマイクロコントローラーでの作業を見ることができます。httpを介した通信だけでなく、たとえば、Emboxのコマンドラインからntpを使用して日付を設定したり、もちろん周辺機器を処理したりすることにも注意してください。
独立して、記事に記載されているすべてのものは、ウィキの指示に従って複製することができ ます
結論
この記事では、美しいインタラクティブサイトを開発してマイクロコントローラーで実行できることを示しました。さらに、ホストのすべての開発ツールを使用して簡単かつ迅速に実行し、マイクロコントローラーから実行できます。当然のことながら、サイトの開発はプロのWebデザイナーが行うことができ、組み込みの開発者はデバイスのロジックを実装します。これは非常に便利で、市場投入までの時間を節約します。
当然、これはあなたが支払う必要があります。はい、SSEはSSIよりもわずかに多くのリソースを必要とします。しかし、Emboxの助けを借りて、最適化せずにSTM32F4に簡単に適合し、128KBのRAMしか使用しませんでした。彼らはそれ以下のことは何もチェックしませんでした。したがって、オーバーヘッドはそれほど大きくありません。そして、開発の利便性とサイト自体の品質ははるかに高くなっています。そして同時に、もちろん、現代のマイクロコントローラーが著しく成長し続けていることを忘れないでください。結局のところ、デバイスはますますインテリジェントである必要があります。