2

RubyでBuffalo/玄人志向の赤外線学習リモコンを操作する

11月22-23日にSFC ORFというSFCの研究室の発表会があって、そこでデモするブツに使う部品としてBuffaloの赤外線学習リモコンのRubyラッパーを書きました。これで家に帰る前に暖房つけたりできてうれしいですね。

玄人志向のKURO-RSとBuffaloのPC-OP-RSを操作できます。

MacとLinuxと、試してないけどたぶんWindowsでも動く気がする。コードはgithubに置いてる

学習リモコン/KURO-RS/(1)使ってみる – 脳みそ沸騰中!のPerlのコードを参考にしました。

■PC-OP-RSを買う
気がついたらKURO-RSがどこにも売ってないんだけど、PC-OP-RSがKURO-RSと同じハードウェアで箱と付属ソフトが違うだけなのでどちらでも動かせます。

BUFFALO PCastTV2対応 PC用学習リモコンキット PC-OP-RS1
バッファロー (2006-09-10)
売り上げランキング: 4538



■インストール
gem install kuro-rs


そしてKURO-RSをMacやLinuxに刺す。
Windowsの人はドライバを入れてBuffaloアプリが動くことを確かめてからUSBポートに刺す。
KURO-RSはUSBポートに接続するとシリアルデバイスとして認識されます。MacやLinuxなら/dev/tty.usb****という名前になる。WindowsならドライバをインストールしていればCOM1とかCOM2とか名前がつく。


■赤外線リモコンを学習する
kuro-rsというコマンドがgemと一緒にインストールされています。

terminalで実行する
% kuro-rs /dev/tty.usbserial-0012a3b4
そして15秒以内に赤外線をKURO-RSの本体に当てると、termialに16進数で赤外線データが出力されます。


学習した赤外線を発射しましょう
% kuro-rs /dev/tty.usbserial-0012a3b4 ffffffff0300f0e0018083...
のように第二引数に16進数でデータを与えます。

これでテレビのチャンネルが切り替わるはず。


■自分のプログラムから使う
こんな感じでIOっぽく使えます。くわしくはexamplesディレクトリを見るといい。
require 'rubygems'
require 'kuro-rs'

# open KURO-RS
kr = KuroRs.open('/dev/tty.usbserial-0012a3b4')

# read
puts kr.read
## => hex dump (ffffffff0300f0e0018083...)

# write
kr.write 'ffffffff0300f0e0018083'

# close
kr.close

# block
KuroRs.open('/dev/tty.usbserial-0012a3b4'){|k|
k.verbose = true
puts k.read
## => hex dump (ffffffff0300f0e0018083...)
}


■Webアプリから使う
シリアルポートは複数プロセスで共有できないし、また複数スレッドから同時に書き込んだりもできないのでRailsやSinatraアプリにkuro-rs gemをそのまま組み込むのはオススメできません。

そういう時はkuro-rs-serverを使いましょう。


こうすると8080番portでhttpサーバーが起動します
% kuro-rs-server /dev/tty.usbserial-0012a3b4 --port 8080


curlから赤外線読み書きしてみる
# read
% curl 'http://localhost:8080'
# => hex dump (ffffffff0300f0e0018083...)


# write
% curl -d 'ffffffff0300f0e0018083...' 'http://localhost:8080'
readもwriteも正常に行われるとstatus 200が返ります。失敗すると200以外。

kuro-rs-serverを内部APIとしてRailsやSinatraから、あるいはAjaxから使うのが良いと思います。

0

canvasで画像処理

html5のcanvasのピクセル単位の処理を試してみた。ここで動かせる → http://dev.shokai.org/test/canvas/cv/

ソースはここ https://github.com/shokai/js-canvas-test/tree/master/cv


輪郭抽出とかしてみた
html5 canvas edge detect


■canvasでのピクセル処理方法
canvasはgetContext(‘2d’)してからgetImageDataするとImageDataオブジェクトが手に入る。
あとの処理はこんな感じ(画像をグレースケールにする例、cv/main.jsの39行目あたり

var canvas = $('canvas#img');
var ctx = canvas[0].getContext('2d');

var img = ctx.getImageData(0, 0, canvas[0].width, canvas[0].height);
for(var i = 0; i < img.data.length; i+=4){
var r = img.data[i]&0xFF;
var g = img.data[i+1]&0xFF;
var b = img.data[i+2]&0xFF;
// var a = img.data[i+3]&0xFF;
var gray = (r+g+b)/3;
img.data[i] = gray;
img.data[i+1] = gray;
img.data[i+2] = gray;
}
ctx.putImageData(img, 0, 0);
データが1次元配列になっていて、RGBAの順に値が格納されている。なのでImageData.dataの要素数はピクセル数の4倍になる。
x,y座標でデータを取れる関数が無いので、自分で色々試して作ってみたけどどうしても3〜6倍ぐらい遅くなってしまうので1次元配列のまま扱う事にした。
JavaScriptで多値を扱うには配列やオブジェクトを使わなければならないんだけどどうやらそれらの生成コストがかなり高いらしく、1ピクセルずつ処理したらひどく遅くなった。


■クロスドメイン
別ドメインの画像をcanvasに読み込んで、getImageDataはできない。chromeだと “Uncaught Error: SECURITY_ERR: DOM Exception 18” というエラーが開発パネルに出る。
ローカルでの開発時にローカルのhtmlからローカルの画像を読み込んでgetImageDataしてもこのセキュリティエラーが出るので、rubyで簡単なhttpサーバーを作っておいたのでコレ使うといい。
実行するとそのディレクトリでhttpサーバーが起動する。


■実行速度
端末によって実行速度が違う。

下の画像は、今年の2月に買った11インチMacbook Airのchromeで、1000×1000の画像を4段階量子化したところ。
だいたい50ミリ秒で量子化できた。遅いパソコンでも毎秒数回は実行できそう。
iPodTouch 4Gや初代iPadはMacbook Airより30倍ぐらい遅くて2秒弱かかる。Nexus Sが意外なことに更に遅くて25秒ぐらいかかる。Macの500倍遅い。
html5 canvas qunatize

0

canvasとwebsocketでお絵かき共有

http://canvas.shokai.org

HTML5のcanvasを使ってみたかったので作った。
websocketも使っているので、chromeかsafariで動く。ブラウザ2つ開いてみると、同期しているのがわかりやすい。

画像のURLを末尾につけるとその画像が読み込めて、マウスで線を引くとみんなでリアルタイムに描ける。Gyazoと合わせて使うと便利。
http://canvas.shokai.org/http://gyazo.com/365d02afdf4953d40ec904df5019aa13.png


ソースはgithubに置いた https://github.com/shokai/shared-canvas


こんな風に、オンラインゲームのマップを置いて、友達と「ここ攻めろ」みたいな指示を共有したいなと思って作ってみた。
http://canvas.shokai.org/http://gyazo.com/1b2f1f6b0df60aa2d0dfe3da79106a4e.png



canvasのプラグインは色々あるけど、canvasのAPIの操作感が変わりすぎる物ばかりだったので、とりあえず何も使わないで操作してみた → draw.js

Imageオブジェクトで画像を読み込んで、onloadイベント発生時にcanvasのサイズを変更するとぴったりのサイズになる。かならずdrawImage前にサイズ変更をすること。
92行目あたり

var draw_img = function(img_url, onload){
var img_tag = $('canvas#img');
ctx = img_tag[0].getContext('2d');
var img = new Image();
img.onload = function(){
img_tag.attr('width', img.width).attr('height', img.height);
ctx.drawImage(img, 0, 0, img.width, img.height);
if(onload && typeof onload == 'function') onload();
};
img.src = img_url; // 読み込み開始
};


rubyとem-websocketで作ったwebsocketサーバーは、daemontoolsで自動起動・復活するようにしてある。DBは無くて、最近10万本のlineの色と太さと座標データを保存している。
また線にはどの画像URLの線か、というデータも付いているので、これで画像URL毎に部屋分けを行っている。

0

縦分割してgrepできるwebsocket client、あるいはADKのデバッグ方法

AndroidでADK開発している時に便利なツールを作った → http://dev.shokai.org/js/websocket-client/

ADKをAndroidに挿すと、1つしかないUSBポートが埋まってしまって、logcatでデバッグ情報を見れなくなってしまう。しかし、俺の使っているNexusSはネットワーク越しのADB(Android Debug Bridge)はrootを取らないと使えない。

素晴らしいことにブラウザからlogcatを見る – 明日の鍵で、「LogcatSocketServer」というlogcatを全てwebsocketに流してくれるサーバーを作ってくれていた。これは凄く良い。

俺は普段はlogcatを複数開いて、 ^[DE]/ やアプリ名などでgrepしてエラーだけを表示したりしている。LogcatSocketServerの出力を分割してそれぞれgrepできるしくみがあれば、普段と同じ快適なAndroidアプリ開発環境が手に入る・・・ので作った。



webブラウザでwebsocketを受信して、縦分割で複数表示し、それぞれgrepできる。(クリックで拡大)
「+」ボタンを押す毎に分割パネルが増える。
この例では4分割して、左から順に「全て表示」「IOIOに関するものだけ表示」「DebugとErrorだけ表示」「バッテリー情報を表示」している。

Androidのデバッグだけではなく普通のwebsocketを使ったwebアプリのテストにも使えるはず。



■LogcatSocketServerのインストール
ブラウザからlogcatを見る – 明日の鍵にあるLogcatSocketServerをAndroidにインストールする。


バイナリがあるのでダウンロードして、AndroidにUSBケーブルつないでインストール。

wget http://tomorrowkey.googlecode.com/svn/trunk/LogcatOnBrowser/LogcatSocketServer/bin/LogcatSocketServer.apk
adb install -r LogcatSocketServer.apk

起動させ、start serverボタンを押すとAndroidがwebsocketサーバーになる。



■webブラウザでlogcatを見る
http://dev.shokai.org/js/websocket-client/を開く。Google ChromeかSafariでしか動かない。

LogcatSocketServerの画面に表示されているIPアドレスとポート番号を、左上に入力してに接続する。
「+」ボタンを押すとパネルが増える。あとはgrep欄に正規表現を書けば表示をフィルタできる。

^[VEW]/アプリ名 とかでgrepしたりすると便利ですぞ。


ローカルで実行するならgithubに置いたのでどうぞ https://github.com/shokai/websocket-client
git clone git@github.com:shokai/websocket-client.git
open websocket-client/index.html


こういうGUIアプリ、VisualStudioとかで作ってたけど無名関数をばしばし使えるJavaScriptの方が作りやすいですね。

1

IOIO for Androidのアナログ入出力を試した

IOIOの10bit ADコンバータと10kHz PWM出力を試した。

今回のコード https://github.com/shokai/android-ioio-study/tree/master/AnalogInOut


Androidの画面上のシークバーを操作すると、PWMによる擬似的なアナログ出力でLEDの明るさを調整できる。
ブレッドボード上のCdSで明るさを計測して、Androidの画面上のシークバーの長さで表示したりした。
R0016190


動画




■準備
まず久しぶりに秋葉原に行ったら秋月が改装工事中だったので、他の店を見て回った。IOIOの開発者が使っているピンヘッダを千石で発見したので買った。

このピンヘッダ気に入った


このピンヘッダ、みっしりと敷き詰めるように装着できる。いつも使ってるメスピンヘッダは隣のピンにぶつかるので敷き詰められない。
ピンヘッダ装備IOIO

これでブレッドボードに線を引き出す準備が整いました


■回路を作る
ブレッドボードに5Vか3.3Vと、GNDを引き出す。
4番ピンでデジタル入力を試すために、タクトスイッチを通して5Vを流し込む。IOIO内部でGNDにプルダウンするので直結で良い。ただし、Read This Before Connectingに書いてある通り5Vを入れて良いピンは決まっているのでよくわからなかったらブレッドボードに出すのは3.3Vにしておいた方が良い。

3番ピンをLEDに接続する。PWMを試すため。

アナログ入力は40番ピン周辺に16個ある。45番ピンにCdSをつけ、10kΩで分圧した。

R0016189



■プログラムを書く
書いた。
AnalogInOutActivity.java

1からAndroidプロジェクトでIOIOを使う方法は前の記事を参考にされたし


このへんのドキュメント参考になる。



重要なのはAnalogInOutActivity.javaの49行目あたり
class IOIOThread extends AbstractIOIOActivity.IOIOThread{
private DigitalOutput led;
private DigitalInput btn;
private PwmOutput pwm;
private AnalogInput ain;
protected void setup() throws ConnectionLostException{
led = this.ioio_.openDigitalOutput(0, true);
btn = this.ioio_.openDigitalInput(4, Mode.PULL_DOWN);
pwm = this.ioio_.openPwmOutput(3, 1000); // 1000hz
ain = this.ioio_.openAnalogInput(45);
}

protected void loop() throws ConnectionLostException{
try{
led.write(!btnLed.isChecked());
if(btn.read()) seekBarDigitalIn.setProgress(1);
else seekBarDigitalIn.setProgress(0);
pwm.setDutyCycle((float)seekBarPwm.getProgress() / seekBarPwm.getMax());
seekBarAnalogIn.setProgress((int) (ain.read() * seekBarAnalogIn.getMax()));
sleep(10);
} catch (InterruptedException e) {
}
}
}

DigitalInputはMode.PULL_DOWNにしておく。PwmOutputは1kHzに設定した(最大10kHz)
PWMもAnlogInputも、0〜1の間のfloat値で入出力値を扱う。Androidの画面のSeekBarは最大値を1000にしておいたので、IOIO上での値を1000倍してintにcastすればSeekBarに表示できる。


デジタル入力の状態をAndroidの画面上に表示する時、IOIOThreadからUI Threadを操作するとデッドロックが起きてアプリが停止するが、なぜかSeekBarはデッドロックが起きないので長さ1の小さなSeekBarを作ってデジタル値を表現するように工夫した。

ほんとうは「android.os.Handlerクラスのpost()を使え」、とyoggyさんが言っていたので今度やってみます。