Download - Web socketドロンくん その後-
WebSocketドロンくん~その後~
『Webアプリによるフィジカルコンピューティング』
SAKURAボードユーザ会 あなたの心もSAKURA色 葉桜の木の下de交流会
2013/4/4
Yuuichi Akagawa
自己紹介
• Yuuichi Akagawa (あかがわ ゆういち)
• USBホストネタ大好き
• 本業は某SI企業でインフラ担当
• 電子工作やプログラミングは趣味での活動
• 仕組みを知ることが好きなので、最終的な作品に至ることがほとんど無い
Copyright©2013 Yuuichi Akagawa 2
OSC2012東京Fallで発表しました
Copyright©2013 Yuuichi Akagawa 3
• WebSocket(Node.js + Socket.IO)を利用した遠隔制御の実験
• Webアプリでフィジカルコンピューティングという提案
• みんなBluetoothでやってるからちょっと斜めで
• 当初はAndroid側をアプリとして実装したものの、後にJavaScriptのみでイケるということに気付く
• 実用性よりも「ネタ」を重視で
• 昨年の東京Node学園祭2012に出せればウケたのに
ちょっとおさらい
Copyright©2013 Yuuichi Akagawa 4
WebSocketとは
• リアルタイムWeb
– 現在多く利用されている、XMLHttpRequest(XHR)によるAjaxポーリングやCometを代表とするロングポーリングではTCPハンドシェイクが都度発生したりHTTPコネクションを長時間占有するなど、Webサーバの負荷が高いという欠点がある。
– これを解決するためにHTML5の一部としてブラウザから直接TCP接続を実現する独自プロトコルの策定が始まった。(現在はHTML5とは独立。RFC6455)
– 2011年末にようやく仕様確定し、メジャーなブラウザでは問題無く利用できる状況にある。
Copyright©2013 Yuuichi Akagawa 5
主要ブラウザのWebSocketサポート状況
プラットフォーム ブラウザ バージョン
Desktop OS Internet Explorer 10以上
Firefox 6以上
Chrome 14以上
Safari 6以上
Opera 12.1以上
Android 標準ブラウザ 未サポート
Chrome 18以上
Firefox 7以上
Opera mobile 12.1以上
iOS Safari 6以上
Copyright©2013 Yuuichi Akagawa 6
ほぼどれでも使える。Android版ChromeはOSが4.0以上で無いと動作しないが、Firefoxであれば2.2以上で使えるので、なんとかなるかと。
Node.jsとは
• サーバサイドJavaScript実行環境
http://nodejs.org
Google V8 JavaScriptエンジン採用
シングルスレッド(JavaScriptだもんね)
イベント駆動(非同期)モデル
nvmというバージョンマネージャやnpmというパッケージマネージャが揃っている
サーバサイドもJavaScriptでコードが書けるのは素敵だと思う
Copyright©2013 Yuuichi Akagawa 7
Socket.IOとは
• クロスプラットフォームリアルタイムWebライブラリ
http://socket.io
Node.js用のライブラリ
サポートプロトコルWebSocketが使えないブラウザでもXHR等に自動フォールバック
• WebSocket
• XHR
• Flash Socket
• JSONP Polling
Copyright©2013 Yuuichi Akagawa 8
Copyright©2013 Yuuichi Akagawa 9
WebSocketドロンくん
ドロンくんとは今岡通博氏考案のAndroid端末による音声認識ロボット。
http://www.ospn.jp/press/20110516no10-useit-oss.html
DTMFによる制御や、ブレッドボードで回路を実装するというお手軽構成。
今回は音声認識の代わりにWebSocket経由でコントロールできるようにしました。
Copyright©2013 Yuuichi Akagawa 10
DTMF (Dual-Tone Multi-Frequency)
• 説明するまでもないとは思いますが、電話の「ピ・ポ・パ」ってやつです
• 正式には「ITU-T勧告Q.24」
• これを4bitのI/Oとして取り出すDTMFデコーダを活用する(2個 ¥300)
高群(Hz)
1209 1336 1477 1633
低群(Hz) 697 1 2 3 A
770 4 5 6 B
852 7 8 9 C
941 * 0 # D
DTMFモータドライバ回路図
Copyright©2013 Yuuichi Akagawa 11
ブレッドボードにちょうど載る規模
Copyright©2013 Yuuichi Akagawa 12
Copyright©2013 Yuuichi Akagawa 13
コマンド送信
HTML5 ready Web Browser(スマホのブラウザでもOK)
Socket.IO module
EC2
コマンド配信(ブロードキャストも可能)
プロトコルはWebSocket
orXHR(Ajax)
DTMF
Node.js + Socket.IOを利用したWebSocketによる遠隔制御
DTMFデコーダ+
モータードライバ
DTMF_0.ogg...
DTMF_#.ogg
Firefox var audio = new Audio(“/sounds/”+file);audio.play()
DTMF音声ファイルダウンロード
サーバサイドコード例
Copyright©2013 Yuuichi Akagawa 14
var express = require('express‘) , routes = require('./routes');
var app = module.exports = express.createServer();
// Configurationapp.configure(function(){
app.set('views', __dirname + '/views');app.set('view engine', 'jade');app.use(express.bodyParser());app.use(express.methodOverride());app.use(app.router);app.use(express.static(__dirname + '/public'));
});
app.configure('development', function(){app.use(express.errorHandler({ dumpExceptions: true, showStack:
true }));});
app.configure('production', function(){app.use(express.errorHandler());
});
// Routesapp.get('/control', function(req, res) {
res.render('control', {title:'Socket.IO Control'});});app.get('/recv', function(req, res) {
res.render('recv', {title:'Socket.IO Receiver'});});
// Socket.IOvar io = require('socket.io').listen(app);var chats = [];var sockets = {};
// broadcast functionfunction broadcast(method,message) {
for (var n in sockets) {sockets[n].emit(method,message);}chats.pop;
}
io.of('/in').on('connection', function(socket) {
sockets[socket.id] = socket;socket.on('control.add', function(data) {
data.time = Date.now();chats.push(data);broadcast('control.add', data);
});socket.on('disconnect', function() {
delete sockets[socket.id];});
});
app.listen(8001);
app.js
これだけでWebサーバとして動作する。
ここがサーバ実装部分
・コネクション受付・データ受信・データ配信全部やってる。
fork()とかpthread_create()とかなにそれって感じ。
操作画面ページコード例
Copyright©2013 Yuuichi Akagawa 15
script(src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js')script(src='/socket.io/socket.io.js')script(type='text/javascript')
// add chat logfunction addlog(data) {var date = new Date(data.time);console.log(date);$('li#log').replaceWith( '<li id="log">' +data.cmd+ ' (' + date.getHours() + ':' + date.getMinutes()+ ':' + date.getSeconds() + ')</li>');
}var socket = io.connect('http://example.com:8001/in');
socket.on('control.add', function(data) {addlog(data);
});function send(data) {socket.emit('control.add', {cmd:data} );return false;
}
h3 WebSocketドロンくんdiv
tabletrtd
input(type='button',class='btn',value='FwdL', onClick='send(1)')td
input(type='button',class='btn',value='Forward', onClick='send(2)')td
input(type='button',class='btn',value='FwdR', onClick='send(3)')trtd
input(type='button',class='btn',value='Turn L', onClick='send(4)')td
input(type='button',class='btn',value='Stop', onClick='send(5)')td
input(type='button',class='btn',value='Turn R', onClick='send(6)')trtd
input(type='button',class='btn',value='BkL', onClick='send(7)')td
input(type='button',class='btn',value='Back', onClick='send(8)')td
input(type='button',class='btn',value='BkR', onClick='send(9)')div
ulli#log
views/control.jade
Express(Webフレームワーク)+ Jade(テンプレートエンジン)を利用。
※http://example.com:8001/controlにアクセスするとこれを返す。
制御用ページコード例
Copyright©2013 Yuuichi Akagawa 16
script(src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js')script(src='/socket.io/socket.io.js')script(type='text/javascript')//DTMF mapvar DTMFtable = [];DTMFtable['1'] = '4';DTMFtable['2'] = '5';DTMFtable['3'] = '1';DTMFtable['4'] = '6';DTMFtable['5'] = 'D';DTMFtable['6'] = '9';DTMFtable['7'] = '8';DTMFtable['8'] = '0';DTMFtable['9'] = '2';//DTMFaudiovar DTMFaudio = null;var SOUND_PATH = "/sounds/";// Get supported sound formatvar AUDIO_EXT = (function(){
var audio = new Audio();var ext = "";if (audio.canPlayType("audio/ogg") == 'maybe') { ext="ogg"; }return ext;
})();//Sound playvar play = function(tone) {
// Get Filenamevar name = 'DTMF_' + DTMFtable[tone];var file_path = SOUND_PATH + name + '.' + AUDIO_EXT;if(DTMFaudio != null){
DTMFaudio.pause();DTMFaudio.currentTime = 0;DTMFaudio = null;
}DTMFaudio = new Audio(file_path);DTMFaudio.play();
};
//Sound playvar play = function(tone) {
// Get Filenamevar name = 'DTMF_' + DTMFtable[tone];var file_path = SOUND_PATH + name + '.' + AUDIO_EXT;if(DTMFaudio != null){
DTMFaudio.pause();DTMFaudio.currentTime = 0;DTMFaudio = null;
}DTMFaudio = new Audio(file_path);DTMFaudio.play();
};
var socket = io.connect('http://176.34.45.101:8009/in');
socket.on('control.add', function(data) {play(data.cmd);
});function send(data) {
socket.emit('control.add', {cmd:data} );return false;
}
h1 HTML5 Delonkun
views/tank.jade ※http://example.com:8001/tankにアクセスするとこれを返す。
今後の予定
• Android端末側
Android端末のカメラの画像をWebSocketで送信して操作画面に表示したい。
音声ファイル再生ではなく、Web Audio APIで自力発音したい。
• サーバ側スクリプト
操作側と操作される側のペアリングを実現したい
Copyright©2013 Yuuichi Akagawa 17
で、それから半年が過ぎた。
Copyright©2013 Yuuichi Akagawa 18
要素技術の動向
• WebRTC (getUserMedia)
Desktop OSでのサポート状況はまずまず
AndroidではChromeβとOpera mobileで利用可能
• Web Audio API
Desktop OSのサポート状況はまずまず
Androidでは未だ使えない
iOSのSafariではサポート→DTMFダイヤラの実装例あり!
Copyright©2013 Yuuichi Akagawa 19
主要ブラウザのサポート状況
プラットフォーム ブラウザ WebScoketAudioのAutoPlay
WebRTCgetUserMedia
Web Audio API
Desktop OS Internet Explorer ○ ○ × ×
Firefox ○ ○ ○ ×
Chrome ○ ○ ○ ○
Safari ○ ○ × ○
Opera ○ ○ ○ ×
Android 標準ブラウザ × × × ×
Chrome ○ × △ ×
Firefox ○ ○ × ×
Opera mobile ○ × ○ ×
iOS Safari ○ × × ○
Copyright©2013 Yuuichi Akagawa 20
2013/4/3現在
主要ブラウザのサポート状況
プラットフォーム ブラウザ WebScoketAudioのAutoPlay
WebRTCgetUserMedia
Web Audio API
Desktop OS Internet Explorer ○ ○ × ×
Firefox ○ ○ ○ ×
Chrome ○ ○ ○ ○
Safari ○ ○ × ○
Opera ○ ○ ○ ×
Android 標準ブラウザ × × × ×
Chrome ○ × △ ×
Firefox ○ ○ × ×
Opera mobile ○ × ○ ×
iOS Safari ○ × × ○
Copyright©2013 Yuuichi Akagawa 21
どちらかが両方サポートされてないと困る。
2013/4/3現在
○○
○×
××
××
×
モバイルでの対応はもう一息
• Desktop OSでは動作可能なレベル
• ○が2つ付いてるブラウザが無いんだな
• Androidはブラウザ毎にWebRTCかAudio APIのどちらかしかサポートされていない
• しかも、Audioのautoplayに対応しているのはFirefoxだけ→その他ブラウザではユーザの介入が必要
Copyright©2013 Yuuichi Akagawa 22
Chromeβ for Androidに実装してみる
• 映像取得処理
getUserMedia()でカメラと接続
カメラからの映像をCanvasに描画
CanvasのデータをtoDataURL()でエンコード
上記で取得したデータをそのままWebSocketで送出
• 音声再生
コマンドに応じて、Audioのsrcにパスを設定
autoplay非対応なので、最初だけ再生ボタンを押す必要あり
Copyright©2013 Yuuichi Akagawa 23
Copyright©2013 Yuuichi Akagawa 24
コマンド送信
HTML5 ready Web Browser(スマホのブラウザでもOK)
Socket.IO module
さくらのVPS
コマンド配信
DTMF
WebSocketを利用したJavaScriptによる遠隔制御
DTMFデコーダ+
モータードライバ
DTMF_0.ogg...
DTMF_#.ogg
Chrome for Android
DTMF音声ファイルはキャッシュマニフェストを利用してローカルに保存
キャプチャ画像送信
キャプチャ画像配信
WebRTCを利用して内蔵カメラからの映像を取得する
急激な円安進行により、AWSが割高になったのでさくらのVPSに引っ越し
まとめ
• JavaScriptだけでなんとか画像配信までできた
• なぜか画像がモノクロだけど
• ちょっと荷が重いので高レイテンシーだぞ
• あと半年くらい待てば、Androidでもフル機能がサポートがされるのではないかと期待
• WebRTCがきちんと実装されれば、WebSocetではなくStreaming APIで滑らかな映像配信ができるはず
• 次回はWeb Audio APIでDTMFもね❤Copyright©2013 Yuuichi Akagawa 25
マイコンもコンパイラもいらない
Webアプリでフィジカルコンピューティング
。*:゜☆ヽ(*’∀’*)/☆゜:。*。
Copyright©2013 Yuuichi Akagawa 26
おしまい
Copyright©2013 Yuuichi Akagawa 27