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

2016年8月16日火曜日

PHILIPS Hueを67個制御してみた


暫く更新をサボってしまいました。

今回機会に恵まれて、会社のFabLabを開設するプロジェクトに参加して照明システムを作ってみました。フロア全体の設計はNOIZ ARCHITECTSさんにお願いしています。

約400平米のフロア全体にPHILIPS Hueを67個(うち2個はHue light ribbon 6m)入れて、Hue Bridge2台をRaspberryPiにいれたnode.jsでWebアプリを立てて制御するシステムを組んでいます。


GUIはこんな感じで、右半分で制御対象のランプを矩形、もしくは領域選択ボタンで指定して左側のColorPickerで色と明るさを指定するというものです。
ColorPickerの下のボタンは色を登録しておくボタン、SCENE SELECTボタンは全体の状態を登録しておくボタンです。




こちらは管理者向け画面で、SCAN LIGHTSでlinkされてないHue lampを探して左側にオレンジの丸で表示し、それをDragすると実物のランプが赤くフラッシュし続けるので右画面のマップ上の位置にDropするとその位置に登録します。67個を如何に手を抜いて設置&設定をするかを考えた末のUIです。
マップ上のランプの色の違いはつながっているBridgeの違いを表しています。



Hueのスターターキット(第2世代 Bridgeが四角のHomeKit対応のもの)を2セット借用して自宅で色々APIを叩いてなんとかなるだろうと高をくくっていたのですが、67個を制御しようとするとそう簡単ではなく色々とうまくいかないところが出てきてスケジュールぎりぎりにようやく動かすことが出来ました。
Googleで検索しても数個を制御している例しか見つからないので、引っかかったポイントを記しておきます。

1.一度Hueのランプを1つのHue Bridgeとペアリングさせると、Delete ligthsで登録を削除しても他のHue Bridgeとペアリング出来ないことがある。(出来ることもあったので条件が何かあるのかもしれません。)

やってみたこと
 ・Hue Bridgeを2台使用して1台目のBridgeとペアリングして制御できる状態のランプをDelete lightsして、2台目のBridgeでSearch new lightsでbodyを空の状態で検索
  -> なにも見つからない
 ・同じく2台目のBridgeでSearch new lightsでbodyにランプのシリアルNoを指定して検索
  -> ランプが点滅して繋がったように見えるが、検索結果には何も見つからない
 ただ、不思議な事に1つのランプだけは他のBridgeに移動することが出来た
 スターターキットはBridgeとランプ3個が既にペアリングされた状態になっているので、結果的に片方のBridgeには4個がペアリングされた状態になり、もう片方には2個がペアリングされた状態になってしまいました。時間切れでこの件はこれ以上追っていません。

2.HueのランプはAPIからはUniqueIDやBridgeに登録されているLightの番号は引けるけど、Search new lightsではランプの側面に書かれているシリアル番号が必要になる可能性があるため取り付け前にシリアル番号を控えておく必要がある。
 今回は、ランプが全て設置された状態からセットアップだったので全て外して確認するのは大変だと思ったのですが、Search new lightsでbodyが空でも全てのランプが検索できたので外さずに済みました。

3.数が多いので制御対象を都度group登録してstateを変更して制御しようとしたけど、連続してAPIを叩くと色々とエラーになって指定してないランプの色まで変わってしまったり、ところどころ色が変わらなかったりする。特にon/offの制御を取りこぼすと1つだけ点いていたりして目立つのでちょっとNGかな。
 -> 結局、groupでの制御はやめて、set light stateで個別制御を制御対象全てのランプに投げてエラーが返ってきたものについてはretryを3回ほど投げる仕様にして、その後2秒間GUIの制御が無い場合は全ランプの最終stateの状態を2度投げることでリカバリーすることにした。これはSuccessが返ってきているにもかかわらず、Stateが正しくないランプがあったので念の為にこうしている。

色々と書きましたが、色の均一性とか表現範囲はHueが一番かと思います。これだけ並べて色が均一に出ているのが素晴らしいです。




2014年10月3日金曜日

RaspberryPiのha1control daemonの更新

使わない機能を削除したり、新しい機能を追加したりしているうちにコードがかなり汚くなってきてしまいました。少しづつ掃除も兼ねて見なおしているのですが、なかなか進みません。
今年になってからの主な機能的な変更点は以下のとおりです。

・HA2module/RP-1 RIR基板のAVRマイコンFW updateに対応
 AVRマイコンのコードをbootとアプリケーションFWに分離して、アプリの方はXBee経由でupdate出来るようにしました。bootの方はAVR用の書き込み治具で直接書き換える必要がありますが、こちらはupdate時に使用されるだけなので書き換える必要は特にないと思います。ha1control daemonの起動時にconfig fileで指定したファイルのバージョンとHA2module/RP-1 RIR基板のAVRのFWバージョンを比較して新しいものがあればupdateされます。

・HA2module/RP-1 RIR基板の赤外線リモコン受信に対応
  AVRマイコンのFWに赤外線リモコン受信機能を追加して、受信したらコード化してha1control daemonに通知するようにしました。

・HA2moduleのAVR側のADCサポート
 これまでXBeeのADCを使ってきたのですが、電圧範囲が0~1.2Vで分圧しないと使えなかったりするものが多く0~3.3Vの方がいいなと思っていました。AVRマイコンの方のADCはリファレンス電圧を色々選べてHA2moduleでは3.3VにしているのでこちらのADCも使えるようにしてHoneywellのHIH-5030という湿度センサーをつなげるようにしました。

・LEDTape用のコマンド追加(ledコマンド)
 以前書いたLEDTapeを制御できるようにコマンドを追加しています。リモコンやスイッチのセンサーなどのイベントでLEDTapeを点灯させたり出来るようにしています。

・HA2module/RP-1 RIR基板の情報表示コマンド追加(devinfo)
 FW update対応したことで各デバイスのFWのバージョン情報や動作ステータス、機能情報などを見られるコマンドを追加しました。

・リモコン受信イベント対応
 AVRマイコンで赤外線リモコンを受信したらha1control daemonで何かコマンドを実行する機能を追加しました。適当なリモコンコードをに名前をつけて学習させてconfig fileでそのコード名で実行するコマンドを記述することで赤外線リモコン対応でない電動シャッターや玄関の電気錠などを赤外線リモコンで制御することが可能になります。RaspberryPi本体の赤外線受信機能では残念ながらこの機能は動きません。これは現状の赤外線受信機能のPCM受信部の実装がデバイスドライバではなくユーザー空間で実装したことで常に受信状態にしておくと負荷が大きいためです。デバイスドライバでDMACを使った実装に変更すれば出来そうですがHA2moduleがあるので後回しにしています。

・リモコンコードを学習リモコンに登録するために回数指定でリピートする機能追加
 リモコン受信イベントに追加する時に手持ちの赤外線リモコンだと他の機器が動いてしまうので都合が悪い場合、ha1control daemonで管理しているリモコンコードを適当にコピーして編集することで、他の機器が動かない独自のコードを作成できます。このコードを学習リモコンに登録することで色々使えるようになるのですが、学習リモコンは学習するリモコンコードを連続して受けることを想定しているようなので1回だけコードを送ると登録してくれません。そこでリモコンコードを回数指定して送ることが出来る機能を追加しました。

これらに付随してコマンドの構造を変更したりQueue管理のbugを修正したりという地味な部分に時間がかかってしまってます。
まだまだやりたいことが色々あるのですが現状リモコン受信その3と同じものですがここに置いてあります。

2014年8月21日木曜日

子供用のスイッチボックス

1歳の息子が壁の電気のスイッチに興味をもって、頻繁にスイッチを押すために抱っこをせがむので子供用にスイッチボックスを作りました。 



よく、自治体の子育て施設とかに行くと壁にスイッチを沢山つけたものとか設置してあるので、子供は興味を持つものなのですね。
押してもなにも反応しないとすぐ飽きられてしまうので、中にモジュールをいれて単1電池で電源を取るようにしました。
これをコントロールサーバーとXBee経由で接続しLEDテープをランダムな色で光らせるようにしました。
中身はこんな感じです。


XBeeを間欠モードで動かすことで消費電力を抑えているので多分半年ほどは電池交換なしで行けるかと思います。
投げられても電池が外れないように固定して、新聞紙で間を埋めてガムテープで箱を閉じてます。
一見、ただ段ボール箱にスイッチを付けただけでどこにも繋がってないように見えるのに実際にスイッチとして動くのが面白いかと思ったのですが1歳の子供には理解できないですね。


2014年8月17日日曜日

LEDTape

PHILIPSのHueのライトリボンをみて作ってみようと思い部品を探してみました。
スイッチサイエンスにフルカラーシリアルLEDがあったので試しに購入。簡単に点くかと思ったら意外とタイミングが厳しく、8MHz動作のAVRの場合割り込み禁止してアセンブラでクロック数を数えながら無駄なく書かないとうまく点きませんでした。
写真は明るいキッチンカウンターの上でとっていますが、LEDが強すぎてLED以外の部分が真っ暗になってしまってうまく撮れなかったので見た目に合わせて色補正かけています。実際に見るとかなり眩しいです。



このLEDは1m60個のフルカラーLEDで、今回は半分に切って50cm30個で使っています。
本当は1灯ずつフルカラーで色を指定できるのですが、実際に使うとなると全体を同じ色で光らせれば十分なのでLEDの個数とR,G,Bを指定してPORTC4につないだシリアルLED を光らせる関数を作りました。
この関数を呼ぶと30us x LEDの個数分割り込み禁止になります。
1m品の場合60個なので1.8msの間blockされます。UARTでフロー制御していない場合、AVRはFIFOが1段しかないので4800bps以下に設定しないと通信を取りこぼします。
HA2moduleで接続しているので、XBeeを4800bpsで接続してコントロールサーバー経由で制御するようにしています。

void SetLED(unsigned char count, unsigned char r, unsigned char g, unsigned char b) {

  asm volatile(
               "  in __tmp_reg__,__SREG__ \n" // interrupt block 30us x count
               "  cli                     \n"
               "  in r6, %[port]          \n"
               "  or r6, %[tmp]           \n"
               "  mov r5, r6              \n"
               "  eor r5, %[tmp]          \n"
               "  mov r2, %[byteg]        \n"
               "loop:                     \n"
               "  ldi %[tmp], 8           \n" // 1
               "loopg:                    \n"
               "  out %[port], r5         \n" // 1
               "  nop                     \n" // 1
               "  sbrs r2, 7              \n" // 1 - 2
               "  out %[port], r6         \n" // 1
               "  lsl r2                  \n" // 1
               "  mov r3, %[byter]        \n" // 1
               "  dec %[tmp]              \n" // 1
               "  out %[port], r6         \n" // 1
               "  brne loopg              \n" // 2
               "                          \n"
               "  ldi %[tmp], 8           \n" // 1
               "loopr:                    \n"
               "  out %[port], r5         \n" // 1
               "  nop                     \n" // 1
               "  sbrs r3, 7              \n" // 1 - 2
               "  out %[port], r6         \n" // 1
               "  lsl r3                  \n" // 1
               "  mov r4 , %[byteb]       \n" // 1
               "  dec %[tmp]              \n" // 1
               "  out %[port], r6         \n" // 1
               "  brne loopr              \n" // 2
               "                          \n"
               "  ldi %[tmp], 8           \n" // 1
               "loopb:                    \n"
               "  out %[port], r5         \n" // 1
               "  nop                     \n" // 1
               "  sbrs r4, 7              \n" // 1 - 2
               "  out %[port], r6         \n" // 1
               "  lsl r4                  \n" // 1
               "  mov r2, %[byteg]        \n" // 1
               "  dec %[tmp]              \n" // 1
               "  out %[port], r6         \n" // 1
               "  brne loopb              \n" // 2
               "  dec %[count]            \n" // 1
               "  brne loop               \n" // 2
               "  out __SREG__,__tmp_reg__\n"
               : 
               : [count] "r" (count),
                 [byter]  "r" (r),
                 [byteg]  "r" (g),
                 [byteb]  "r" (b),
                 [tmp]    "r" ((1 << PORTC4)),
                 [port]   "I" (_SFR_IO_ADDR(PORTC))
               : "r0", "r2", "r3", "r4", "r5", "r6");
}