jQuery コードリーディング その1

jQueryソースコードを読んで、
jQueryjavascriptを勉強する連載(予定)。
jQueryを使ったjavascriptプログラミングの勉強ではないです。

モチベーションはこれ

最近はオープンソースにもいいものがあります。例えば、jQueryは僕がMicrosoftの外に出てから、一番美しいと思ったコードですね。

ブログ主のプログラム関連スペックはこんな

  • Cはだいたいわかる
  • Railsはさわったことがある。Herokuで自分用に動かしている簡単なアプリがある。
  • javascriptは見よう見まねでコードを書いたことがある。生涯コード記述量は300行くらい。

jQueryのバージョンは現時点で最新の2.1.1を使う。

いつ終わるかわからないが、終わらせるまでやりたいと思う。

Pebble でWorldCupを楽しむ

2014 FIFA World Cup Brazil の試合スコアをライブ表示するPebble Appをつくってみた。
スコアが動くとブルッとします。多分動くと思う。
PCな方はこちら
Android/iOSな方はこちら
f:id:zinziroge:20140613230656p:plain

このあたりを使って作っています。感謝。

基本的にはこれと同じです。
Pebbleでサッカースコアライブ更新表示してみた - じんじろげのジロジロモニタ

RaspberryPiにいろいろつないでみた

この記事は書きかけです

RaspberryPiを

  • ラジオサーバ
  • 猫監視
    • WebCamでブラウザ経由監視
    • WebCamを2軸サーボで動かす
    • 室温監視してエアコンOn/Off

にすることを目的にいろいろつなげるだけつないでみた。

RaspberryPiに

  • ロータリーエンコーダ x2
  • LED
  • トグルスイッチ
  • 2.2inch LCDモニタ
  • デジタル温度計(1-Wire)
  • Arduino(ATMega328P-PU)

をつないでみた。
RaspberryPiにつないだArduino(ATMega328P-PU)に

をつないでみた。
RaspberryPiとArduino(ATMega328P-PU)の間は、SPI(スケッチ書き込み用)とシリアル通信でつないでみた。
各種デバイスをRaspberryPi, Arduinoのどっちにつなぐかはかなり適当です。
当初は、5V系デバイスはArduino, 3.3V系はRaspberryPiにするつもりでしたが必ずしもそうはなってないです。

GPIOでArduinoへスケッチ書き込み

ArduinoというかATMega328への書き込みと言った方が正確かも。
Gertboard | Arduino IDE Installation (ISP) | Gordons Projects
を実施

シリアル通信でArduinoと接続

ちょっとはまった。
何か送信できるけどうまく取り込めない。3.3V(RaspberryPi) <-> 5V(ATMega328)変換で結線ミスかPullUpが弱いのかと思って悩んだ。が、結局は、Getboard(という評価ボード)が12MHzなので、16MHzでATMega328を動かす場合は設定ファイルを書き換える必要があった。
以下を変更。

/usr/share/arduino/hardware/arduino/boards.txt:

gert328.name=Gertboard with ATmega328 (GPIO)
...
#gert328.build.f_cpu=12000000L
gert328.build.f_cpu=16000000L
...

5V電源はGPIOから供給

RaspberryPiにつないだATMega328P-PUにサーボモータをつないでいるので、RaspberryPiの5V電源で十分な電流が流せる必要がある。
が、microUSB経由だとポリフューズがあるので700mAまでしか流せない(実際、microUSB電源供給でサーボモータを動かすとすぐ落ちる)。
色々調べるとGPIOの5Vに直結するのが良さそう。
なのでmicroUSBから電源供給しないで5V 2A のAC/DCアダプタをGPIO 5Vに直結することにした。

デジタル温度計

ここを参考に実装。
Raspberry Pi で温度センサ(DS18B20+)を読んでみた - 完全に個人的な備忘録
# 1-wirearduinoで使えるようにもトライしてみたい気もするが

この記事は書きかけです

Chromecastを買いました

Chromecastを買いました。

HDMIポートを持つテレビにつないで使います。
chromecastに対応したアプリの情報をchromecastに飛ばしてテレビに表示させることができます。
セットアップは、PCをWiFiネットワークにつなげたことがあれば問題なくできると思うので省略します。
chromecastの機能のポイントは、

  1. chromecastに対応したアプリしかテレビに表示させることができない
  2. 対応アプリでは、PC/スマホからchromecastに映像を送るのではなく、chromecastがそのコンテンツをインターネットから持ってくる

です。(と、理解している)
ただ、2つ目に関しては、ローカルに保存した動画をPCのchromeブラウザで再生したらTVで見られたので、chromeブラウザで表示できるものはなんでもTVで見れると言う方が正しそうですね。でも、音がたまに途切れたりしたので、コンテンツによっては厳しそう。

全体構成のイメージはこんな感じです。
f:id:zinziroge:20140531155919p:plain

できることとかをまとめるとこんな感じだと思います。
勘違いがあれば、ご指摘ください。

現在できること

できないかと思いきやできること

できないこと

  • DVDをPCで再生してTVでみる
    • コンテンツが自分のPCにしかないものは無理。もちろん、DVDと同じコンテンツがインターネット上から見れるなら話は別です。
  • 手持ちの動画ファイルをPCで再生してTVでみる
    • 理由は同上と思っていたのですが、chromeブラウザで再生できるフォーマットだったらTVでみれた

#12/10追記 まだ正式機能ではないようだけど、デスクトップを転送できるので、↑は一応できるみたい。

ちょっと頑張るとできること

今のところのとても満足してます。
ひとつうれしい誤算だった(あんまりちゃんと考えいていなかったところなのです)のは、chromeで再生中のニコ動をchromecastに飛ばしてから、PCで別の作業を初めてもTVではニコ動が表示され続けるってことですね。
サブモニタ的な使い方もできる感じですね。

Pebbleでサッカースコアライブ更新表示してみた

Pebble Smartwatch | Smartwatch for iPhone & AndroidSimply.jsででなんかできないかなぁと考えてみた。
わたし、サッカーをよく見るのですが、今開催されている試合の結果がリアルタイムで分かったら何か楽しそうなので、作ってみることにしました。
ワールドカップを来月開催されることだし。

まず、サッカースコアのライブ更新しているサイトを探すと以下が良さそう。
LiveScore Soccer : Live Soccer Scores by LiveScore.com
欧州リーグだけでなくJ1,J2,J3(!)もカバーしている。
もちろんワールドカップも。

このサイトから情報を取得するためのスクレイピングは、Javascriptに不慣れなのと、一度使って見たかった簡単にスクレイピングしてWebAPI化してくれるという
kimono : Turn websites into structured APIs from your browser in seconds
を使ってみたかったので、これでいく。

15分くらいで直感的にWebAPIができました。
以下がそのWebAPIで取得できるJ1の試合結果のJSONファイル。
https://www.kimonolabs.com/api/52h5eq42?apikey=664315ac6dd2128d90351faeb721fb63

また、Simply.jsの使い方は
ひとりぶろぐ » 2.0で面白くなったスマートウォッチPebbleを、JavaScriptゴリゴリでネット監視装置として使う
を参考にさせてもらいました。

あと、開発はCloudPebbleを利用してブラウザ上で実施。
ソースはこんな感じ。
ごちゃごちゃ書いてあるけど、やっていることは

  • Config用URL(スマホ上で)で情報を取得するリーグを選択
  • 60秒毎にスコア再取得。スコアはスマホ側に記録する。
  • スコアが前回と変わっていたらvibeする。

#試し試しやっててかなり汚いです
#あと、タイムゾーンがあってないです

var api_uri = 'http://www.kimonolabs.com/api/52h5eq42?apikey=664315ac6dd2128d90351faeb721fb63';
var match_text = '';

//simply.title('J1 League');

//simply.style('small');
simply.scrollable(true);

//simply.on('singleClick', function(e) {
function refresh(opt) {
	if( opt.length > 0 ) {
		var league = opt.match(/kimpath2=(.*)&kimpath3=(.*)/);
		console.log('league[0]=' + league[0]);
		console.log('league[1]=' + league[1]);
		console.log('league[2]=' + league[2]);
		if( league.length == 3 ) {
			opt = '&kimpath2=' + league[1] + '&kimpath3=' + league[2];
		} else {
			opt = '';
		}
	} else {
		opt = '';
	}
	
	api_uri_opt = api_uri + opt;
	ajax({ url: api_uri_opt, type: 'json' }, function(data) {
        var matches = data.results.collection1;
        var match_days = data.results.collection2;
		
		if( league.length == 3 ) {
			match_text += league[1] + '/' + league[2] + '\n';
		}
		
		for(var i=0; i < match_days.length; i++) {
			match_text += match_days[i].match_day + ',';
		}
		match_text += '\n';
		
		for(var i=0; i < matches.length; i++ ) {
			if( matches[i].score == "? - ?" ) { // not live
				score = ' ' + matches[i].score + ' '; 
			} else { // live
				score = ' ' + matches[i].score.text + ' '; 
			} 
			match_text +=
				matches[i].match_time + ': ' +
				matches[i].home_team_name + 
				score +
				matches[i].away_team_name + 
				'\n\n';
		}
		simply.text({
			body    : match_text
		});
		
		match_status = window.localStorage.getItem('match_status');
		if( match_status != match_text ) {
			simply.vibe();
		}
		window.localStorage.setItem('match_status', match_text);
		
	});
}

simply.begin = function() {
	try{
		var options = window.localStorage.getItem('options').split('/');
		if( options.length > 0 ) {
			for(var i=0; i < options.length - 1; i++) {
				console.log('opt is = ' + options[i]);
				refresh(options[i]);
			}
			setInterval(refresh, 60000);				
		}
	}catch(e){
		concole.log(e);
	}
};

// https://github.com/pebble-hacks/js-configure-demo/blob/master/src/js/pebble-js-app.js
Pebble.addEventListener("showConfiguration",
	function(e) {
    	console.log("showConfiguration!");
		Pebble.openURL('https://dl.dropboxusercontent.com/u/9794530/pebble_soccer_live/config.html');
	}
);

Pebble.addEventListener("webviewclosed", function(e) {
	console.log("configuration closed");
	// webview closed
	var options = JSON.parse(decodeURIComponent(e.response));
	var opt_str = '';
	console.log("Options = " + JSON.stringify(options));
	for(var key in options) {
		if( options[key] == true ) {
			var path_list = key.split('/');
			console.log('&kimpath2=' + path_list[1] + '&kimpath3=' + path_list[2] + '\n');
			opt_str += '&kimpath2=' + path_list[1] + '&kimpath3=' + path_list[2] + '/';
		}
	}
	window.localStorage.setItem('options', opt_str);
});

で、Pebbleで表示はできたんだけど1つ問題が。
1分毎にスコアを更新したいのだけど、下記記事によると
Pebble Javascript Tips And Tricks

setInterval(refresh, 60000);

はあまりよくないらしい。

でも、上の記事にあることをやろうとすると、Simply.jsでは力不足で、
WatchAppをCで、SmartPhone側のアプリをJavascriptで書く必要があるみたい。

う~ん。

Raspberry Pi で監視カメラ

Raspberry PiにUSBのWebCameraをつけて室内の監視カメラにしてみました。

WebCameraはこれ

LOGICOOL ウェブカム HD画質 120万画素 C270

LOGICOOL ウェブカム HD画質 120万画素 C270

アプリは motion.

sudo apt-get install motion

使い方はここを参考に設定しました。
Linux + motion + webcamでライブ監視カメラを設置する

設定ファイルは /etc/motion/motion.conf。
変更した設定はこのあたり。
#この記事を書くために見直していたらtimelapse撮影/動画生成もあるみたい

# 静止画(JPEG)はbest shotのみ保存。
output_normal best
# 別PCからWebCamera映像を見れるように
webcam_localhost off
# 制御は別PCからできるけどuser/passwordで制限をつける
control_localhost off
control_authentication user_name:password

これで
http://192.168.0.2:8081/ とかでWebCameraの映像が見れるようになります。

ただ、監視カメラの目的が自分が不在時の部屋の監視なので、自分が家にいるときは撮影しなくていい。
また、そのうちドメインを取って外部から見れるようにしたいので、なおさら自分が部屋にいるときは撮る必要がない。
# セキュリティ的な見直しは必要だけれども

そこで、「自分が部屋にいるか判定」にスマホBlueTooth接続の有無で判断するにした。
部屋にいるときは、motion を stop、いないときは motion を start させる。
まず、BlueToothアダプタはこれを購入。

PLANEX Bluetooth USBアダプター Ver.4.0+EDR/LE(省エネ設計)対応 BT-Micro4

PLANEX Bluetooth USBアダプター Ver.4.0+EDR/LE(省エネ設計)対応 BT-Micro4

インストールとスマホとのペアリングは、ここを参照して実施。特に問題なく完了。
Raspberry PiでBluetoothを扱う - Programming Log

自分で仕組みを作る前にぐぐってみると、デスクトップをオフ(スクリンセーバーモードに移行?)できるアプリはあった。ただ、今回の目的には使えなさそう。
第267回 Bluetoothデバイスで離席管理する:Ubuntu Weekly Recipe|gihyo.jp … 技術評論社

「自分が部屋にいるか判定」にスマホBlueTooth接続の有無で判断する
のにどうすれば一番いいかよくわからないけど、
力技で l2ping でスマホBlueToothから応答があれば、部屋にいると判定して motion のプロセスを止めることにした。こんな感じ。これをrootのcronで適当な間隔で回す。

#!/bin/bash

# 実際はスマホのbluetoothアドレスを設定
bt_addr='XX:XX:XX:XX:XX:XX'

resp_bt=`l2ping ${bt_addr} -c 1|grep ' 0% loss'`

if [ ${#resp_bt} != 0 ]; then
        echo "bt remote device exist."
        service motion stop
else
        echo "bt remote device doesn't exist."
        resp_motion=`ps auxww|grep '^motion.*/usr/bin/motion'`
#       echo ${#resp_motion}

        if [ ${#resp_motion} = 0 ]; then
                echo "motion isn't running."
                service motion start
        else
                echo "motion is aleady running."
        fi
fi

# もっとスマートに書けると思うのだが...

2014/02/01 追記

  • if-else文の中身が逆になってた(orz)のを直しました。すいません。
  • /var/run/motion/motion.pid ファイルが存在している場合は、motion が起動していると判断する方がセオリーぽい。

2014/04/29 追記

  • l2pingだとなぜか反応がないことがあるのでhcitool nameに変更。自分の環境ではこっちの方が安定動作しています。
  • motionの起動チェックは、/var/run/motion/motion.pidの存在チェックに変更
#!/bin/bash

# 実際はスマホのbluetoothアドレスを設定
bt_addr='XX:XX:XX:XX:XX:XX'

#resp_bt=`l2ping ${bt_addr} -c 1|grep ' 0% loss'`
resp_bt=`hcitool name ${bt_addr}`
#echo ${#resp_bt}

if [ ${#resp_bt} != 0 ]; then
	echo "bt remote device exist."
	service motion stop
else
	echo "bt remote device doesn't exist."
#	resp_motion=`ps auxww|grep '^motion.*/usr/bin/motion'`
#	echo ${#resp_motion}

	if [ -e /var/run/motion/motion.pid ]; then
		echo "motion is aleady running."
	else
		echo "motion isn't running now."
		service motion start
	fi
fi