前回の記事では Arduino I/O を Web API で操作する方法について説明しました。今回はそのWeb API を利用して、スマートフォンから Arduino I/O を操作するWebアプリを紹介します。このアプリは、HTML(+CSS) と JavaScriptだけで作成していますので、対応する Webブラウザであれば様々な端末から操作できると思います。下記はこのWebアプリを iPod touch で動作させたときの表示画面です。
このWeb アプリでは、Arduino I/O ピンの出力をチェックボックスで ON/OFF に設定できます。画面下の CLEAR ボタンを押すとポート全体の出力を全て OFF にすることができます。 Webアプリを起動した時には、現在の Arduino のポートの出力値がチェックボックスに既に反映されています。Web アプリを複数の Web ブラウザから同時に操作した場合には、Reload ボタンを押して現在の I/O値を再ロードすることができます。
Web アプリで使用するファイルについて説明します。HTML ファイルと CSSファイル、JavaScript ファイルの3つのファイルで構成されています。Webブラウザ側にこれらのファイルを配信するために、DeviceServer に搭載されている HTTP サーバー機能を利用します。Web API を実行する DeviceServer のPC とHTTPサーバーを別のPC に分けて設置することもできます。”C:\Program Files\AllBlueSystem\WebRoot” フォルダがDeviceServer に搭載されている HTTP サーバーのデフォルトのルートディレクトリになります。そのフォルダの下に “arduino_io” フォルダを作成して以下の3つのファイルを配置します。これらのファイルはここからダウンロードすることができます。
HTML ファイル(index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Arduino I/O コントロール</title>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<link rel="stylesheet" href="my.css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
</script>
<script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js">
</script>
</head>
<body>
<div data-role="page" id="page1">
<div data-theme="a" data-role="header">
<a data-icon="refresh" id="reloadBtn">Reload</a>
<h3>Arduino I/O</h3>
</div>
<div data-role="content" data-theme="a">
<div class="ui-grid-a">
<div class="ui-block-a" data-theme="a">
<div data-role="fieldcontain">
<fieldset data-role="controlgroup" data-type="vertical">
<legend>
PORT 0
</legend>
<input name="checkbox1" id="checkbox1" type="checkbox" class="pin" pin="2"/>
<label for="checkbox1">
DIO#2
</label>
<input name="checkbox2" id="checkbox2" type="checkbox" class="pin" pin="3"/>
<label for="checkbox2">
DIO#3
</label>
<input name="checkbox3" id="checkbox3" type="checkbox" class="pin" pin="4"/>
<label for="checkbox3">
DIO#4
</label>
<input name="checkbox4" id="checkbox4" type="checkbox" class="pin" pin="5"/>
<label for="checkbox4">
DIO#5
</label>
<input name="checkbox5" id="checkbox5" type="checkbox" class="pin" pin="6"/>
<label for="checkbox5">
DIO#6
</label>
<input name="checkbox6" id="checkbox6" type="checkbox" class="pin" pin="7"/>
<label for="checkbox6">
DIO#7
</label>
</fieldset>
</div>
<a data-role="button" class="port" port="0">
CLEAR
</a>
</div>
<div class="ui-block-b" data-theme="a">
<div data-role="fieldcontain">
<fieldset data-role="controlgroup" data-type="vertical">
<legend>
PORT 1
</legend>
<input name="checkbox7" id="checkbox7" type="checkbox" class="pin" pin="8"/>
<label for="checkbox7">
DIO#8
</label>
<input name="checkbox8" id="checkbox8" type="checkbox" class="pin" pin="9"/>
<label for="checkbox8">
DIO#9
</label>
<input name="checkbox9" id="checkbox9" type="checkbox" class="pin" pin="10"/>
<label for="checkbox9">
DIO#10
</label>
<input name="checkbox10" id="checkbox10" type="checkbox" class="pin" pin="11"/>
<label for="checkbox10">
DIO#11
</label>
<input name="checkbox11" id="checkbox11" type="checkbox" class="pin" pin="12"/>
<label for="checkbox11">
DIO#12
</label>
<input name="checkbox12" id="checkbox12" type="checkbox" class="pin" pin="13"/>
<label for="checkbox12">
DIO#13
</label>
</fieldset>
</div>
<a data-role="button" class="port" port="1">
CLEAR
</a>
</div>
</div>
</div>
</div>
<div data-role="page" id="error_back_dialog">
<div data-role="header" data-theme="e">
<h3>script error</h3>
</div>
<div data-role="content" data-theme="e">
<h3>エラーが発生しました</h3>
<p>サーバー処理中にエラーが発生しました。現在のセッションが無効になっている場合があります。サーバー側にログが残されていますので確認して下さい</p>
<p><a data-role="button" data-inline="true" data-icon="check" data-rel="back" data-theme="e">OK</a></p>
</div><!-- /content -->
</div>
<script src="main.js"></script>
</body>
</html>
今回のWeb アプリでは、GUI コンポーネントに jQuery-mobile を利用していますので <script> タグでこれらのライブラリを最初にロードしています。index.html ではメイン画面のコンポーネントとエラー発生時のダイアログの定義を行っています。メインの動作ロジックはJavaScript ファイル main.js に記述しています。
JavaScript ファイル(main.js)
// Webサーバーから、別のPC で実行中の DeviceServerにアクセスする場合の
// URLホスト名とポート番号部分を設定する
// DeviceServer 自身の HTTP サーバー機能を使用している場合には "" にする
//
//var server_host_url = "http://your_DeviceServer_public_url:80";
var server_host_url = "";
// DeviceServer セッション認証用トークン文字列
var session_token = "";
// DeviceServer の LogService にログメッセージを出力する
function log(msg,module){
var url = server_host_url + "/command/log?message=";
url = url + encodeURIComponent(msg);
if (module != undefined){
url = url + "&module=" + encodeURIComponent(module);
}
$.get(url,"",function(data){},"text");
}
// DeviceServer のスクリプトを実行する。
// 第2パラメータにスクリプトパラメータを指定する
// callback パラメータを省略するとリターンパラメータを受け取らない
function script_exec(name,params,callback){
if (callback == undefined){
var callback = "default_callback";
var url = server_host_url + "/command/json/script" +
"?session=" + encodeURIComponent(session_token) +
"&resultrecords=0" +
"&name=" + encodeURIComponent(name);
} else {
var url = server_host_url + "/command/json/script" +
"?session=" + encodeURIComponent(session_token) +
"&name=" + encodeURIComponent(name);
}
for(key in params){
url = url + "&" + encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
}
$.ajax({"url" : url,
"dataType" : "jsonp",
"jsonpCallback" : callback
});
}
// script_exec() でコールバックを指定しない場合のデフォルトコールバック関数
function default_callback(data){
if (data.Result != "Success"){
log("*ERROR* script error!");
return;
}
}
// スクリプト実行結果ステータスのみをチェック
function script_exec_callback(data){
if (data.Result != "Success"){
$.mobile.changePage( "#error_back_dialog", {transition: "pop",role:"dialog"});
}
}
// スクリプト実行結果ステータスチェックとリロード
function script_exec_callback_reload(data){
if (data.Result != "Success"){
$.mobile.changePage( "#error_back_dialog", {transition: "pop",role:"dialog"});
}
load_arduino_io();
}
// UI コンポーネントの xml 属性値を検索取得
function getAttrVal(node,name){
var val = "";
var attr = node.attributes;
for (var i=0; i<attr.length; i++){
if (attr[i].nodeName == name){
val = attr[i].nodeValue;
}
}
return val;
}
// ARDUINO_IO_GET スクリプト実行結果のイベントハンドラ。
function apply_ui(data){
var pval;
if (data.Result != "Success"){
$.mobile.changePage( "#error_quit_dialog", {transition: "pop",role:"dialog"});
return;
}
if (data.ResultParams.PORT_0 != ""){
pval = parseInt(data.ResultParams.PORT_0,16);
$("#checkbox1").attr("checked",((pval & (1 << 2)) != 0)).checkboxradio("refresh");
$("#checkbox2").attr("checked",((pval & (1 << 3)) != 0)).checkboxradio("refresh");
$("#checkbox3").attr("checked",((pval & (1 << 4)) != 0)).checkboxradio("refresh");
$("#checkbox4").attr("checked",((pval & (1 << 5)) != 0)).checkboxradio("refresh");
$("#checkbox5").attr("checked",((pval & (1 << 6)) != 0)).checkboxradio("refresh");
$("#checkbox6").attr("checked",((pval & (1 << 7)) != 0)).checkboxradio("refresh");
}
if (data.ResultParams.PORT_1 != ""){
pval = parseInt(data.ResultParams.PORT_1,16);
$("#checkbox7").attr("checked",((pval & (1 << 0)) != 0)).checkboxradio("refresh");
$("#checkbox8").attr("checked",((pval & (1 << 1)) != 0)).checkboxradio("refresh");
$("#checkbox9").attr("checked",((pval & (1 << 2)) != 0)).checkboxradio("refresh");
$("#checkbox10").attr("checked",((pval & (1 << 3)) != 0)).checkboxradio("refresh");
$("#checkbox11").attr("checked",((pval & (1 << 4)) != 0)).checkboxradio("refresh");
$("#checkbox12").attr("checked",((pval & (1 << 5)) != 0)).checkboxradio("refresh");
}
}
// UI コンポーネントの初期値ロード
function load_arduino_io(){
script_exec("ARDUINO_IO_GET",{},"apply_ui");
}
// メインページが表示された
$( '#page1' ).live( 'pageshow',function(event){
// 秘密のセッショントークン文字列を強制的に設定
session_token = "abc123";
load_arduino_io();
});
// チェックボックスを操作してArduino ピンの値が更新された
$('input[class="pin"]' ).bind( "change", function(event, ui){
var params = {};
var attrVal = getAttrVal(this,"pin");
if (attrVal != ""){
params["pin"] = attrVal;
if (this.checked){
params["value"] = "1";
} else {
params["value"] = "0";
}
script_exec("ARDUINO_PIN_SET",params,"script_exec_callback");
}
});
// ボタンを操作してArduino ポートの値がクリアされた
$('[class="port"]' ).bind( "click", function(event, ui){
var params = {};
var attrVal = getAttrVal(this,"port");
if (attrVal != ""){
params["value"] = "00";
params["port"] = attrVal;
script_exec("ARDUINO_IO_PUT",params,"script_exec_callback_reload");
}
});
// リロードボタンが操作された
$( "#reloadBtn" ).bind( "click", function(event, ui){
load_arduino_io();
});
この JavaScript ファイルで Arduino をリモートから操作する Web API をコールしています。index.html ファイルで表示したメイン画面のチェックボックスやボタンをユーザーが操作したときに、この main.js で定義されたイベントハンドラが実行されます。jQuery のセレクタ式で対象となる GUI コンポーネントを選択して各イベントを処理しています。ファイルの最初の部分で、 DeviceServer 側のLua スクリプトを実行するための Web API をコールするscript_exec() 関数を定義しています。この関数では JavaScript の連想配列形式で指定されたスクリプトパラメータを URL パラメータに変換して、Web API をコールしています。
また、サーバー側の認証を省略するために作成したセッショントークン文字列を session_token 変数に設定しています。
前回の記事中の動作確認画面で、Web ブラウザ中に表示された JSON 文字列を参照しながら main.js を見ていただけると、 Web API のパラメータとリターン文字列の処理内容が分かり易くなると思います。
CSS ファイル(my.css)
.text-align-center {
text-align: center;
}
.text-align-right {
text-align: right;
}
jQuery-mobile で利用する CSS ファイルです。
以上の3つのファイルを ”C:\Program Files\AllBlueSystem\WebRoot\arduino_io” フォルダに配置すると、Arduino を Web ブラウザから操作できる様になります。また、前回の記事で説明した Arduino 側で実行するスケッチと、Web API で実行するサーバー側の Lua スクリプトファイルは事前に設定しておいて下さい。
Web ブラウザで “http://localhost:8080/arduino_io/index.html” にアクセスすると、Webアプリのメイン画面が表示されて、現在の Arduino I/O の状態がチェックボックスに表示されます。チェックボックスを操作して、Arduino I/O を ON/OFF に設定できます。”localhost” 部分は、スマートフォン等からアクセスする場合には DeviceServer のIP アドレスやホスト名に置き換えて指定して下さい。
また、操作中に何らかの原因でエラーが発生した場合には下記の様なエラーダイアログが表示されます。
以下に、Web アプリを操作した時の動画を載せましたので参照してください。(音量注意)
ここで紹介した DeviceServer の機能は、ABS-9000 DeviceServer インストールキットをダウンロードして、直ぐに使用することができます。(デモライセンスが添付されていますので直ちに使用可能です)
それではまた。

