前回の記事では 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 インストールキットをダウンロードして、直ぐに使用することができます。(デモライセンスが添付されていますので直ちに使用可能です)
それではまた。