ラベル bonescript の投稿を表示しています。 すべての投稿を表示
ラベル bonescript の投稿を表示しています。 すべての投稿を表示

2015年2月11日水曜日

Beaglebone black(bonescript)からメールを送る

サランラップ+お湯で柔らかくなるプラスチックによる雑な仕事w

まず、SMTPサーバを入れてその設定、次にBoneScriptを書く。

SMTPサーバの入れ方はこちら。とても参考になりました、ありがとうございます。
記事との相違点はMacではなくBeagleboneなので「brew/aptitudeの替わりに apt-get を使う」ことと、設定ファイルの中身です。蛇足ですが、fromとuserは同じでOKです。設定したら記事にあるように、テストしてみてください。

で、BoneScriptで書いてみます。Node.jsから直接SMTPを呼び出せるmailなどのモジュールも試してみたのですが、エラーの山を解決することができなかったので、child processとして呼び出す方式にしました。




メール系のテストをするとこうなるよねw

さて。

この度、玄関にもPIRセンサーを取り付けました。恐ろしいことに15mのワイヤ(余ってた6芯の電話線)による有線接続ですw センサーをほどよくナナメに固定するのは結構難しいのですが、玩具として売られているお湯で柔らかくなるプラスチックを使いました。80度のお湯に3分ほどつけておくと練り消しゴム状態になり、好きな形に成型してから冷します。プラスチックというよりもスーパーゴムのような素材なので、パッキングがわりに使ったり、断線しやすいiPhoneコネクタの補強、壊れた自転車用パーツの補修などに使えると思います。これでまた3Dプリンタの購入が遠ざかってしまいました(笑)。

これだけ入って1080円

なお、お湯を保温性のあるカップにいれて作業場所まで持っていくのがコツです。PIRセンサーを覆うように形を作っておき、反対側を冷たいドアに押し付けると丁度いい角度に調整することができます。私は面倒くさいので養生テープで止めちゃいましたが、プラスチックの中に磁石埋め込めばそのまま固定できると思います。電子工作において、「ナナメ固定」は結構鬼門ですが、これはホントに便利です。安いし。

養生テーブが泣かせます
先日の「自室での人間の動きを記録するアプリ」をちょっと改造して、来客を検知したらメールを送るようにしてみました。

これで留守番中に間違ってドアチェーンをかけてしまって外出中のヨメを締め出してしまっても大丈夫だ(ごめん)。

うちは宅配便ぐらいしか来ない&廊下の端で他の住民が通らないから通知ごとに送るようにしていますが、人通りの多いところでは一度送ったら数分間は検出しても送らない、などの工夫が必要かもしれないですね。


2015年1月12日月曜日

Play!アプリとBoneScriptの通信 - 3.BoneScript

「BBBとは?」みたいな余計な説明がなくて中身が濃い。

というわけで、Play!アプリとBoneScriptの通信、完結編です。

  1. 構成
  2. Playアプリ
  3. BoneScript

■BoneScript■

以前、BoneScriptで7セグメントLEDを駆動したら、毎秒170桁しかダイナミック点灯させることができずにちらつきました。その際には各セグメントのon/offもループしていたのでおよそ毎秒7x170回はループしていた、ということになります。ダイナミック点灯は無理でも毎秒10回程度のサンプリングなら余裕ですね。8bit時代のBASICマイコンで空のFOR〜NEXTループを回すと250回ぐらいだったかなぁ(遠い目)。

ということで、以下ソースです。


特に目新しいことはしていません(できません)。line 11で100m秒ごとのインターバルを設定してchechPIRを呼び出し、digitalReadのコールバックとしてprintStatusを呼び出しています。

printStatusでは100m秒ごとにサンプリングして入力がHIGHならcountDetectedをインクリメントし、「1分間のうち何秒間、PIRセンサがHIGHを返してくるか(なおPIRセンサは負論理で動きを検出するとLOWになるのですが、トランジスタでレベル変換した時に正論理に変わってます)」を求めています。結果はCSVとしてファイルに書き出し(line no 56 - 62)、JsonとしてWeb APIに送っています(line no 64 - 94)。

その処理自体は簡単なのですが、起動直後の中途半端な「分」を無視したり(prevMinute==99)、「睡眠記録」として作ったので昼の12時でファイル名が切り替わるようにしたりで(line no 44 - 54)、ちょっと面倒になっています。

なお、日付書式の変換には下記で公開されている「formatDate」を使わせていただきました。ありがとうございます。上記ソースを試す場合には末尾にformatDateをコピペしてください。
Qiita : 日付フォーマットなど 日付系処理

■今後の予定■

現状ではセンサーを追加したらDBの定義からやり直さなければいけないですが…それはなんぼ何でもIoTっぽくないので、定義を抽象化するなりシリアライズして「思いついたらすぐセンサーを追加してグラフ化」という路線を目指してみましょうか。

そういえばXivery申し込んだんだけど、返事が来ない…まぁXiveryを使っていたら、Play!でグラフ化するなんてことをやろうとは思わなかったでしょうから、むしろ感謝すべきかなw

2015年1月4日日曜日

PlayとD3とBeaglebone



会社でのお仕事として年明けからPlay+Spring+Emberを使ってサイトを1つ立ち上げることになっているのですが、どれもチュートリアルをなぞった程度で本格的に使ったことありません。しかし:

  • 休み中はしっかり休みたい。
  • でも勉強しておかないと死ぬのは確実。
  • でも休み中は電子工作方面に浸りきりたい

ので、この3つを満たすために「Beagleboneで収集したデータをPlay! frameworkで書いたHeroku鯖へ送り、グラフ化して表示」ってやつを作ろうと思います。visualizeには同僚さんに教えてもらったD3jsというフレームワークのグラフがあまりにもきれいなので、それ使います。EmberとSpringのことはとりあえず忘れます。

■動作フロー■

基本的にこんな動き。まずセンサ〜収集系:
  1. Beaglebone Black Rev.c上でPIRセンサーを読み取り人の動きを検出
  2. 検出した結果は1分あたり動いた秒数として出力し、Heroku鯖へ送る
  3. Heroku上で動くアプリはWeb APIとしてデータを受信する。
  4. 受信したデータはHerokuのPostgreSQL上にリングバッファ的に保存(Herokuのポスグレは10k行まで無料で使えるので)。
続いてデータ表示系:
  1. ブラウザからのリクエストを受けてHTMLを返す
  2. HTML上でD3.jsがHerokuへデータをリクエスト、Jsonで返す
  3. D3.jsがグラフを描画
D3.jsはデータ形式も多彩でcsv, tsv, json, xmlなど思いつくものはだいたい入ってます。

■予備実験■

BBB+PIRセンサーは現在絶賛安定稼働中なので、未体験要素としてD3jsでのグラフ生成を試してみます。棒グラフを出すだけならChart.jsの方が圧倒的に簡単なのですが、もうね、表現力が圧倒的すぎるのですよD3様は(そこまで使いこなせるかどうかは別の話)。

ということで、まずチュートリアルからサンプルソースをコピペ。こんな立派なグラフがこれだけで?と思うぐらいソース短い。

■Play 2.3.7■

しばらく自宅MacでPlayをいじっていなかったら、ターミナルでactivatorとタイプしてもエラーになってしまう。のでbrewでアンインストール&インストール(安易だ)。
brew uninstall typesafe-activator
brew install typesafe-activator
で、プロジェクトを作る:
activator new ShowSensors
起動後、play-java templateを選択。戻ってきたらactivator uiを起動
cd ShowSensors
activator ui
しばらくしてブラウザ上にIDEが開く。最近ブラウザIDEが流行ってますねぇ。ただ、エディタがタブ対応でないならviの方がマシだし、何よりcompile/runがうまくいかないので今回も利用を見合わせます。Play2の場合、Eclipseを使うメリットがあんまり感じられないのでsublime text2使います。ctrl+cで終了し、
activator run
ビルドその他のメッセージがおとなしくなったところでブラウザで localhost:9000 を開けばいつものページ、緑色の「Your new application is ready」のメッセージがお出迎えしてくれます。

■ヘンなエラー■

ひと通り実装をしたところで試すとこんなエラーが:

[error] play - Cannot invoke the action, eventually got an error: java.lang.RuntimeException: Error getting BeanDescriptor for path User from models.User
ググってもどうもピンと来ない。何となくEbean系の間違いのようなので、
  • とりあえず@OneToMany / @ManyToOneなどを消して見る
  • とりあえずactivator clean / activator updateなどを試す

などやってみたのですが、ダメ。その後、トレースしてみると、
User user = find.fetch("user").where() .eq("email", inEmail) .findUnique();
ここで落ちてました。そりゃそうだ。ここを
User user = User.fetch().where() .eq("email", inEmail) .findUnique();
に変えたら通りました。先にUserの生成と検索についてのtestを書いてりゃ容易に切り分けが出来ていたケースですね…反省(@元日)。

■で、Web API■

フツーにRoutesにURIとコントローラを書いて、
GET /fetchFromHours controllers.Application.fetchFromHoursGet()
コントローラでdbからデータをfetchし(仮にarrayDataとする)、最後に return ok(Json.toJson(arrayData)); と書けばOKです。以下は過去24時間の計測データを返すAPIの処理:
// curl --header "Content-type: application/json" --request GET http://localhost:9000/fetchFromHours
public static Result fetchFromHoursGet(String inEmail) {
User owner = User.findByEmail(inEmail);
Calendar calFrom = Calendar.getInstance();
calFrom.setTime(new Date());
calFrom.add(Calendar.HOUR_OF_DAY, -24);
calFrom.set(Calendar.MILLISECOND, 0);
Integer hours = 24;
List<SensorData> sds = SensorData
                       .findByUserSinceDateForHours(owner, calFrom, hours);
if (sds == null || sds.size() == 0) {
return badRequest("no data found");
}
List<SensorDataRest> sdr = new ArrayList<SensorDataRest>();
for (SensorData sd : sds) {
sdr.add(new SensorDataRest(sd));
}
return ok(Json.toJson(sdr));
}

■Macにポスグレをインストールして起動■

brew install postgresql
initdb /usr/local/var/postgres -E utf8
postgres -D /usr/local/var/postgres &
これで
psql -l
とやってデータベース一覧(初期状態の設定テーブルが2-3個あるはず)が見えればOK

■Web APIでURLパラメータを渡すには■

今回は使いませんでしたが、http://.........../apiName?name=kura&email=foo@bar.comという処理をしたい場合には、routesに
GET /apiName  controllers.Application.api(name:String, email:String)
と書き、ソースに
public static Results api(String inName, String inEmail) {
....
}
と書けばOKです。routesにはパラメータが省略された時の値を指定することも可能で
GET /apiName controllers.Application.api(name:String ?="kura", email:String ?="tare@panda.com")
と書きます。RESTfulな書き方については簡単に見つかるんですが?&でパラメータを渡す方法がなかなか見つからなかったのでメモしておきます。

■Heroku上のpostgreSQLへ接続■

まずHeroku上のpostgreSQLを有効にします。

すると、URLやid, passwordなどが通知されますので、それに基づいてapplication.confを書き換えてdatabaseをH2からpostgreSQLに変更し

build.sbtにpostgreSQLのドライバを追加します。
libraryDependencies ++= Seq(
  javaJdbc,
  javaEbean,
  cache,
  javaWs,
  "com.fasterxml.jackson.core" % "jackson-databind" % "2.4.4",
  "com.fasterxml.jackson.core" % "jackson-annotations" % "2.4.4"
,
  "postgresql" % "postgresql" % "9.1-901-1.jdbc4"

)
ま、上の設定が決まるまで一発では動いてはくれなかったんですが…その辺は末尾の「ハマりリスト」をご笑覧くださいorz

■Herokuへdeploy■

これはもうここに書いてあるそのまんまです。
  Heroku へのデプロイ(playframework.com)
便利になったものです…初回はdependencyのチェックでエラい時間かかるけど、待ってりゃ終わります。終わったところで

で、以下の様な表示が出ればOK
koichi$ heroku ps
=== web (1X): `target/universal/stage/bin/showsensors -Dhttp.port=$PORT`
web.1:
up 2015/01/04 08:36:10 (~ 2s ago)

大事なのはここのupってところ。これがcrushedだとデプロイor起動に失敗したということ。もう見飽きるほど見ましたよ…ええ…。

■BoneScript変更■

テストではcsvに書き出すだけでしたが、コードを追加しました。まずpayloadを作って
var body = JSON.stringify({
    date:  sDate,
    count: sCount,
    email: "kkurahashi@me.com"
});
POSTします。
var request = new http.ClientRequest({
    hostname: "ホスト名",
    port: 80,
    path: "/receive",
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "Content-Length": Buffer.byteLength(body)
    }
});
request.write(body);
request.end();
通常のPlayアプリだとポートは9000ですが、Herokuにデプロイすると特に設定しなくても80でつながります。他にエラー処理もあるけど、あとでGithubに上げるのでそっちをご覧くださいませ。

■できあがり■

8:48はトイレに行ってた

■今後■

上記は生データ表示なので24時間収集すると表示が細かすぎます。これを5分か15分にサマライズします。あとはローカルとリモートのPostgresをdev/deployで自動的に切り替わるような設定も組み込まないといけません。ぽすぐれと接続できないでウロウロしてた時にその辺の設定は全部後回しにしてしまい、現状はapplication.confを手動で切り替えておりますw

それからソースを掃除してからGithubでプロジェクトを公開します。まぁ今度の週末にでも。

そして以下は今回の作業でハマった箇所です。改めて見直すと…我ながら…アホです。でも失敗して覚える…というか失敗しないと学習しないタイプなので、とっても勉強になりました。正月休みはウォーキングにも行かないでこればっかりやってたせいで3kg太ってけども。

餅は危険です。

■ハマりリスト■

  • テストが作動しない
    • せっかくテストコードを書いたのに activator test を実行しても新しいテストコードが認識されないことがある。そういう場合には、activator cleanを実行すると直る。もしダメなら一度activatorからexitして(もし起動していれば)Eclipseを終了させ、activator cleanを実行してから再度activatorに戻ってtestを試してみる。
    • 全般的にソースを追加した場合にはcleanするかactivatorを一度終了した方がいいかもしれない。また、application.confやsbt関連ファイルを弄った場合にはactivatorを終了/再起動しないとダメっぽい。
    • あと、activatorを起動しっぱなしで半日作業したら「メモリが足りない」ってエラーが出たことがありました。Eclipseばかりではなく、たまにはコンソールも見ましょう。
  • @Constraints.Requiredが機能しない
    • Ebeanでは @NotNull を使うorz
  • @ManyToOne(fetch=FetchType.EAGER)が効かない
    • objをfetchしてきた時、obj.toOneには値が入っているけどobj.toOne.someとやるとnullになってしまう。toOneをprivateに変更し、getter/setterを設けることで解決。
    • StackOverflowには「Play!のバグじゃね?」って意見もあるけど、どーなんでしょうね。
  • 複数項目を対象にしたunique属性
    • @Entityの後に @Table(uniqueConstraints = @UniqueConstraint(name="制約につける名前", columnNames = { "attr1", "attr2" })) ...とカラム名を列挙する。
    • なお、java上のattribute名ではなくdb上のカラム名なので、対象となるattributeには@Columnでカラム名を指定するか、Evolution表示で出てくるカラム名を使うこと。
  • PostgreSQLと接続できない
    • application.confでのURI書式が違ってたorz
      db.default.driver=org.postgresql.Driver
      db.default.url="jdbc:postgresql://localhost:5432/データベース名"
      db.default.user=ユーザ名
      db.default.password=パスワード
  • PostgreSQLでテーブルができない
    • userというテーブル名は予約語なので作れませんorz
    • クラス名を変更するか、@Table(name="users")を追加してテーブル名を変更する。
  • その後、UserをUsersに変更したけど、evolutionで生成されるSQLが古いままでcleanなどをやってもcreate userのまま
    • 中途半端なところでevolutionが止まったため、管理テーブルplay_evolutionsだけがpostgreSQL上に出来ていたのが原因っぽい。
    • ので、drop databaseでまるごと削除したら動いた。
  • 以下、Herokuへデプロイできた後の話
  • コンソールやpsqlで見るとデータがあるのにAPIでfetchできない
    • Herokuがアメリカ時間だったため。以下の記事を参考にして、JSTに変更したら治った。インスタンスの再起動も不要でした。
    • http://blog.ruedap.com/2011/02/10/heroku-timezone-japan-jst
    • コマンドラインで heroku config:add TZ=Asia/Tokyo

2014年6月30日月曜日

Beaglebone Blackで7セグメントLEDデジタル時計

同梱

■約5日で到着

欲しいと思ったのが3月、5月上旬に発売されると聞いてMouser.comに発注したのが4/23、秋月やAdafruitへの入荷を見送って待ち続けてきましたが、Adafruitに再度入荷したので我慢できずに6/25に注文。6/29の11時ごろ配達されたのですが、買い物に出かけていて留守、やっと本日11時半ごろ入手しました。いえー。



■というわけで、7セグメントデジタル時計をまた作った

事前にブレッドボード上に回路を組んであり、Bonescriptでも一応書き終わっていたものの、初めての環境だとやっぱり色々ありますね…。ピン配列の資料を見て、「じゃ、P8の3番以降を使おう」と決めてあったのですが、実際に走らせると「そこのピンはMMCが使っているからダメよ」とエラーが。

間違った配線の図

じゃ、どうすりゃいいのよ、とそれらしい資料を探し出すのに小一時間かかりました。Getting Startedにあったんですけどねorz

やっと1桁表示できたw

それからソフトのバグをプチプチ潰しつつ、「せっかくの時計なんだからちゃんとntpで時間合わせしないと」と思ったのが運の尽きでちょっとハマったり。出来上がったのが、以下のムービーです。


ムービーのフリッカーを合わさって、ほとんど判読不能ですね…。実物はもうちょっと読めるのですが、ともかく実用にはなりません。Cloud9上で90桁/秒程度、コマンドライン上で170桁/秒程度です。170だと普通に読み取れますが、チラついてることが誰にでもはっきりわかるレベルです。

BCDデコーダが手元にないので7セグメントを1ピンずつループまわしてドライブしました。TC4511あたりを使えばもう少し早くなるかもしれませんが、それでも1Ghz ARM上のnode.jsだとこのへんが限界かもしれません。少なくとも、高速化のためにあれこれいじるよりCで書いた方がラクだと思います。

■設定など

SDカードを買い忘れてしまったので、まだなーんにもやってませんw

とりあえず、日付を毎回合わせるのは面倒くさいので、ぐぐって見つかったこちらのページ(Thank you!!)を参考にターミナルで
    ntpdate -b -s -u ntp.jst.mfeed.ad.jp
と実行。何の問題もなく設定された。なお、あんまり当然すぎて書いてないのだと思いますが、この設定はUSBとMac(PC)つないだだけではダメみたいです。DHCPのあるネットにEthernetをつないでからrebootし、USBのコンソールから入ってifconfigでen0のipアドレスを調べ、一度USB経由で接続したシェルからログアウトして、再度en0のIPアドレスにroot@<アドレス>では入ります。