Más contenido relacionado
Similar a Web socketドロンくん その後- (20)
Web socketドロンくん その後-
- 2. 自己紹介
• Yuuichi Akagawa (あかがわ ゆういち)
• USBホストネタ大好き
• 本業は某SI企業でインフラ担当
• 電子工作やプログラミングは趣味での活動
• 仕組みを知ることが好きなので、最終的な作品に
至ることがほとんど無い
Copyright©2013 Yuuichi Akagawa 2
- 3. OSC2012東京Fallで発表しました
Copyright©2013 Yuuichi Akagawa 3
• WebSocket(Node.js + Socket.IO)を利用した遠隔制御
の実験
• Webアプリでフィジカルコンピューティングという提案
• みんなBluetoothでやってるからちょっと斜めで
• 当初はAndroid側をアプリとして実装したものの、
後にJavaScriptのみでイケるということに気付く
• 実用性よりも「ネタ」を重視で
• 昨年の東京Node学園祭2012に出せればウケたのに
- 6. 主要ブラウザの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以上で
使えるので、なんとかなるかと。
- 9. Copyright©2013 Yuuichi Akagawa 9
WebSocketドロンくん
ドロンくんとは
今岡通博氏考案の
Android端末による
音声認識ロボット。
http://www.ospn.jp/press/20110516no10-useit-oss.html
DTMFによる制御や、
ブレッドボードで回路を
実装するというお手軽構成。
今回は音声認識の代わりに
WebSocket経由でコントロール
できるようにしました。
- 10. 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
- 13. Copyright©2013 Yuuichi Akagawa 13
コマンド送信
HTML5 ready Web Browser
(スマホのブラウザでもOK)
Socket.IO module
EC2
コマンド配信
(ブロードキャストも可能)
プロトコルは
WebSocket
or
XHR(Ajax)
DTMF
Node.js + Socket.IOを利用したWebSocketによる遠隔制御
DTMFデコーダ
+
モータードライバ
DTMF_0.ogg
.
.
.
DTMF_#.ogg
Firefox
var audio = new Audio(“/sounds/”+file);
audio.play()
DTMF音声ファイル
ダウンロード
- 14. サーバサイドコード例
Copyright©2013 Yuuichi Akagawa 14
var express = require('express‘) , routes = require('./routes');
var app = module.exports = express.createServer();
// Configuration
app.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());
});
// Routes
app.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.IO
var io = require('socket.io').listen(app);
var chats = [];
var sockets = {};
// broadcast function
function 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()
とか
なにそれって感じ。
- 15. 操作画面ページコード例
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 log
function 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
table
tr
td
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)')
tr
td
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)')
tr
td
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
ul
li#log
views/control.jade
Express(Webフレームワーク)+ Jade(テンプレートエンジン)を利用。
※http://example.com:8001/controlにアクセスするとこれを返す。
- 16. 制御用ページコード例
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 map
var 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';
//DTMFaudio
var DTMFaudio = null;
var SOUND_PATH = "/sounds/";
// Get supported sound format
var AUDIO_EXT = (function(){
var audio = new Audio();
var ext = "";
if (audio.canPlayType("audio/ogg") == 'maybe') { ext="ogg"; }
return ext;
})();
//Sound play
var play = function(tone) {
// Get Filename
var 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 play
var play = function(tone) {
// Get Filename
var 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にアクセスするとこれを返す。
- 19. 要素技術の動向
• WebRTC (getUserMedia)
Desktop OSでのサポート状況はまずまず
AndroidではChromeβとOpera mobileで利用可能
• Web Audio API
Desktop OSのサポート状況はまずまず
Androidでは未だ使えない
iOSのSafariではサポート
→DTMFダイヤラの実装例あり!
Copyright©2013 Yuuichi Akagawa 19
- 22. モバイルでの対応はもう一息
• Desktop OSでは動作可能なレベル
• ○が2つ付いてるブラウザが無いんだな
• Androidはブラウザ毎にWebRTCかAudio APIの
どちらかしかサポートされていない
• しかも、Audioのautoplayに対応しているのは
Firefoxだけ
→その他ブラウザではユーザの介入が必要
Copyright©2013 Yuuichi Akagawa 22
- 23. Chromeβ for Androidに実装してみる
• 映像取得処理
getUserMedia()でカメラと接続
カメラからの映像をCanvasに描画
CanvasのデータをtoDataURL()でエンコード
上記で取得したデータをそのままWebSocketで送出
• 音声再生
コマンドに応じて、Audioのsrcにパスを設定
autoplay非対応なので、最初だけ再生ボタンを押す
必要あり
Copyright©2013 Yuuichi Akagawa 23
- 24. 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に引っ越し