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

Pebble WatchFace開発

年末にPebble WatchFaceを作ってみたので、その備忘録。

  • Pebbleとは?
    • 所謂スマートウォッチ
    • スマホBlueToothでつながる
    • 表示はモノクロe-paper(e-inkではないらしい)ので7日位バッテリーが持つ(自分の実測では5日)
    • ボタンが4つついてるので、これを操作してスマホ側アプリと連携してなんかすることもできる

詳細はこの辺を見て。
公式: Pebble Smartwatch | Smartwatch for iPhone & Android
まとめ: スマートウォッチ「Pebble」まとめ - NAVER まとめ
分解: Pebble Teardown - iFixit
いろんなWatchFace: http://www.mypebblefaces.com/


ここから開発について。

開発関連情報

とにかくここ、こまったらここを見る。
Develop for Pebble

開発環境

自分のPCに環境を作ってもいいですが、Web上でコーディング&コンパイルスマホへのインストールまでできるサイトがあったので、今回はCloudPebbleで開発します。
ちなみにWindowsは今のところ非対応らしい。ただ、Windowsで開発するにせよ、CloudPebbleで開発するにせよ、SDKのアーカイブにサンプルコードが含まれているので、ダウンロードしておいた方が幸せになれます。

SDK

Pebble SDK 1.X系と2.0系があってAPI名とか多分他にもなんかが違う。今から古いのを使うのもなんなんで、2.0を使います。

WatchFace?WatchApp?

WatchFaceはPebbleのボタンを使わないもの、WatchAppはボタンを使えるもの。
BlueTooth通信の有無ではないらしい。

PebbleKit?

iOS, Androidで動くスマホ側のアプリの開発キットらしい。今回は使わないので未調査。

サンプルWatchFace

今回、作ったのは砂時計。ざっくりとした仕様は

  • 1時間で砂が流れ切る
  • 上側と下側の砂の量は、時間と対応付けて表示を変える
  • 砂が流れるアニメーションをつける
  • 砂が流れ切ったら砂時計が1回転する
  • Pebbleを振ると下側の砂が平らになる
// アプリの開始時に実行
static void do_init(void) {
	//割り込みハンドラの登録、リソースの確保、初期化とか
}

// アプリの終了時に実行
static void do_deinit(void) {
	//リソースの開放とか
}

int main(void) {
	do_init();
	app_event_loop(); // おまじない
	do_deinit();
}

…詳細を書こうと思ったけど、
Develop for Pebbleの'Getting Started', 'Pebble Developer Guide'と拙作のソースを見てもらった方が早いなぁ、と思ったり。
きたないけどソースはzinziroge/sandglass_watchface · GitHubに置いてあります。
他にもGithubhttp:// http://www.mypebblefaces.com/でソースが公開されているものもあるのでそれを参考にするのが手っ取り早いと思います。

これで終わるのもあれなのではまったところだけ箇条書き。

時間割り込みの登録

秒、分、時、日、月、年が変わるタイミングで呼ばれるハンドラが登録できるのだが、例えば1秒毎、1時間毎に割り込みをかけようとして以下のように2つハンドラを登録したら1秒毎の割り込みが実施されず。

tick_timer_service_subscribe(SECOND_UNIT, &handle_second_tick);
tick_timer_service_subscribe(HOUR_UNIT, &handle_hour_tick);

正解は、これ。

tick_timer_service_subscribe(SECOND_UNIT|HOUR_UNIT, &handle_tick_timer);

で、ハンドラ側で割り込み要因を判別して処理を分ける。

void handle_tick_timer(struct tm *tick_time, TimeUnits units_changed) {
	if( units_changes | SECOND_UNIT) {
		// 1秒毎に実施する処理
	}
	if( units_changes | HOUR_UNIT )
		// 1時間毎に実施する処理
	}
}
2pixel以上の太い線が描けない

graphics_draw_line() という線を引くAPIがあるが、太さを指定できない(つまり1pixel)。
水平線や垂直線なら graphics_fill_rect() で実現できる。でも、斜め線が描けない。
PebbleにはDrawing Pathsという一筆書きで線を描いて、内部を塗りつぶすこともできるデータ型がある。
これを使って太さと傾きに応じて太線の4頂点の座標を計算すれば、gpath_draw_filled()で描ける。
ちなみにsin,cos,atan2はテーブルで持っていてそれをAPIで参照できるので、上の座標計算もダイナミックにできます。

1つ作ってみての感想

WatchFaceぐらいの規模だと環境作る方がめんどいから、クラウドで開発できるのすごくいいと思う。
エディタもメモ帳レベルだけじゃなくて, vim-like, emacs-likeが選べるのでどっちか使える人には便利。
CloudPebbleは楽チンですばらしいのだが、唯一こまるのがスクリーンショットが撮れないこと(Linux環境だと撮れる(らしい))。
CloudPebble blog | What're we up to?(開発者Blog)を見ると最優先事項だと認識しているらしいので実装されると大変うれしい。

Android で画像処理

OpenCV も使ったり、画像取り扱いの基本

  • OpenCV library を使うために前準備

libs に OpenCV-2.4.7-android-sdk\sdk\java\bin\opencv library - 2.4.7.jar をDrag&Drop
f:id:zinziroge:20131223174100p:plain

OpenCV Managerの呼出
Activityの実行直前に非同期に呼び出す(onResume()で呼び出すのはなんでだろう。onStart(), onCreate()じゃだめ?)
https://groups.google.com/forum/#!topic/android-opencv/LIixgr-iYSs
http://corkboard.ivory.ne.jp/?p=17

  • setContentView()

XMLのレイアウトファイルを使う方法と実行時にViewを作る方法がある
http://www.javadrive.jp/android/activity/index4.html
http://allabout.co.jp/gm/gc/80740/

Android 開発環境整備

Windowsで開発環境を整えて、
実機デバッグ、OpenCVが使えるようになるまで。

Android Studioでやろうと思ったけど、今回は挫折。
Eclipse + ADT Plugin のセットにしたアーカイブ。
http://developer.android.com/sdk/index.html

ADTに含まれるので不要

  • USBドライバー

手持ちのAndroidスマホがSHARP製なので。
https://sh-dev.sharp.co.jp/android/modules/driver/
http://www.adakoda.com/android/000242.html
http://developer.android.com/tools/device.html
http://d.hatena.ne.jp/wize03/20130113/1358069204

http://www.kosaic.jp/wordpress/2012/12/opencv-for-android-2-4-3-2-%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB/
http://answers.opencv.org/question/14546/how-to-work-with-opencv4android-in-android-studio/
http://blog.bbtune.com/archives/1372/compiler-compliance-level-1-5-%E2%86%92-1-6-the-project-was-not-built-since-its-build-path-is-incomplete

  • Android NDK(Native Development Kit)

CとかC++で開発するために必要らしい。特に使う予定はないが、OpenCVのサンプルのいくつかをコンパイルするために必要。
http://developer.android.com/tools/sdk/ndk/index.html

Kindle Paperwhite を買いました

Kindle Paperwhite 3G

Kindle Paperwhite 3G

を買って、2週間くらい使っての感想。
3G版を買いました。Amazon.comで買った Kindle4 も持ってるのでそれとも比較してみます。

いいところ

  1. 喫茶店で本が買える
  2. バックライトがいい

1つ目は、わざわざ外で本を買うことなんてないからwifi版でもいいやと思ってたけど、
喫茶店とかで本をその場で買えるのは結構新鮮でした。
2つ目は、寝床に入って本を読むのにバックライトがなかなかよいです。

いまいちなところ

  1. 電池の持ちはKindle4より悪い
  2. wifi接続でないとメールを取得できない

1つ目の電池の持ちが悪いのは、3Gの待ち受けとバックライトの性だと思うのまぁしょうがないかな。無線を切る機能はあるけど、3Gだけ切れるようにもして欲しかった。
2つ目は自分の調査不足。3GはAmazonで書籍を買うためにしか使えないらしい。
ブラウザがつながらないのは知っていたけどメールも駄目なのね。
スマホで見つけて面白いページをその場でmobiに変換して、kindleに送って見れるかと思ったけど、できませんでした。
Amazonにすれば、何にもお金が落ちてこないから当たり前なのか。

でも、おおむね満足。
#漫画が危険w。手軽すぎて、読み終わったらついつい次の巻も購入してしまう。

Raspberry Pi でRuby

まずは、rvm(Ruby Version Manager)を下記を参考にしてインストール。
RVM
Linux で rvm を用いて Ruby をインストール

curl -L https://get.rvm.io | bash -s stable --ruby

対応するバイナリがないのでソースから自動的にコンパイルしてくれる。が、小一時間かかる。
次にrubygemsをupdate。

gem install rubygems-update

openssl ライブラリがないとかなんとか怒られるので、

sudo aptitude install libopenssl-ruby1.9.1

でいいかなぁと思ったけど駄目だった。ググる

rvm pkg install openssl
rvm pkg install readline

が必要らしい。そんで、rubyをreinstallしろと言われるので素直に従う。

rvm reinstall all --force

で、rubyをソースからコンパイルするので小一時間待つ。
やっとこさrvmのインストール完了。こんな感じ。

pi@raspberrypi ~ $ rvm list

rvm rubies

=* ruby-1.9.3-p374 [ armv6l ]

# => - current
# =* - current && default
#  * - default

次にGPIOをたたくためのライブラリをインストールする。
RPi Low-level peripheralsを見るとRubyでRaspberryのGPIOをたたくには、WiringPiPi Piper の二つがあるらしい。
Pi Piperはイベントドリブン型。
RaspberryのGPIOをラジオのボリューム調整とチャンネル選択に使うので、今回はPi Piperの方がよさげなので、こちらをインストールする。

gem install pi_piper

まずは、ここまで。