ゆっくり技術ノート!

いつかきっとできるだろうよ

Raspberry Pi で家電を操作する1

目次

今回は長くなるので複数回に分ける
  1. Raspberry Pi で家電を操作する1 (MacにWebサーバを設置して照明を操作する)
  2. Raspberry Pi で家電を操作する2 (Raspberry PiにWebサーバを設置して照明を操作する)[未投稿]

やりたいこと

  1. 今回はブラウザから部屋の照明のスイッチをサーボモータで動かしon/offさせたい
  2. 最初は Mac で作ってからRaspberry Pi に移す

構成と環境

Raspberry PiArduino の通信はシリアル通信を使用した
Raspberry Pi から直接サーボを操れるようだが、慣れているので Arduino を使用した。そのうち変更するかもしれない Gyazo

  1. OS X 10.10.2
  2. Arduino UNO
  3. ruby 2.0.0p598
  4. serialport (1.3.1)
  5. sinatra (1.4.5)

作業の軌跡

  1. Arduino でシリアルを受信してサーボを動かす
  2. Sinatra からシリアル信号を送る
    1. 時間差をつけてシリアル信号を送る
  3. Sinatra にタイマー機能をつける
  4. Raspberry Pi に移す
    1. IPを固定する
    2. ホスト名でリモート接続できるようにする
    3. 作成したSinatraアプリケーションをデーモンにする

Arduino でシリアルを受信してサーボを動かす

シリアル信号に応じてサーボを右もしくは左に少し回して照明などのスイッチをon/offする
スケッチは以下のような感じになった

  1. loop で常時シリアル信号を監視する
  2. 信号は数字になっていてそれぞれに対応したアクションを探す
  3. アクションが決定したら実行する
  4. サーボをデフォルトの位置に戻す

codeはgistにある
Arduino のコード

動作はこんな感じになった Gyazo

Sinatra からシリアル信号を送る

Sinatra でブラウザにon/offボタンを表示させ押せれたらシリアル信号を送るとようにしたい
Sinatra とは Ruby on Rails のような Ruby で Webサーバを立てるためのドメイン特化言語(DSL)です

Sinatra でシリアル通信を扱うためにはgemのserialportが必要なのでinstallする
gem install serialport

コードは以下のようになった

  1. 初期動作
    1. textで保存したシリアルポートの名前(環境によって異なる)を読み込む
    2. シリアルのインスタンスを作成
  2. /がリクエストされたらon/offボタンのあるhtmlを送る
  3. スタンドライトのonのボタン/standOnが押されたらシリアルを送信し/にリダイレクトする

見た目も良い感じになったと思う Gyazo

嵌ったところ1 逆引きDNS

localhost:4567でアクセルするとロードが100ms程度で終わるのに対しIPアドレスを直接指定すると1分以上かかってしまった
どうやらSinatra側がアクセスしてきたクライアントのIPアドレスをホスト名に変換するときに hosts が設定されていないとタイムアウトするまで時間がかかってしまうらしい (この解釈であってるかわからない)
DNS逆引きを止めるコードがネット上にあったので使わせてもらうことにした
sinatraでDNS逆引きを止める | TechRacho

嵌ったところ2 inline-block の隙間

今回 inline-block を使ったがリストタグ<ul>内に改行があると改行が半角スペースとなって表示されるらしい
その影響で各ボタンの間に隙間ができてしまって結果的にレイアウトが崩れてしまった

CSS で inline-block の文字間を 0em にして inline-block中 のulの文字間を xx-large とすることで解決した
一部を抜粋

ul{
    list-style:none;
    font-size: 0em;           /* inline-block 隙間対応*/
}

li p, .on, .off, .timer{
    padding: 30px 0px;
    margin: 0px;

    display: inline-block;
    font-size: xx-large;  /* inline-block 隙間対応*/

    color: #FFFFFF;

}

rerun

余談だが今回始めてrerunを使ってみた
rerun は関連するコードが更新されるとsinatraを再起動してくれる sinatra-contribだとイマイチ動こかないことがあったが rerun はいい感じに再起動してくれる
gem install rerunしてrerun 'ruby app.rb'する
Sinatra: Frequently Asked Questions

タイマー機能を作る

今回は簡単に指定時間 sleep させたあと任意のアクションのアドレス(例えばlocalhost:4567/standOn)にアクセスするようにする
コードはこんな感じになった

require "open-uri"

Hour = 3600

#Create mode
print("Select Mode\n1. Stand Light On\n2. Stand Light Off\n3. Room Light On\n4. Room Light Off\n--> ")
mode = gets.to_i

case mode
when 1 then mode = "standOn"
when 2 then mode = "standOff"
when 3 then mode = "roomOn"
when 4 then mode = "roomOff"
else
    print("Mode error\n")
    return 0
end
print("Select #{mode}\n\n")

#Create timelimit
print("Select Time(hour)\n--> ")
limit = gets.to_i
print("Select #{limit}hour\n\n")

#Wait
print("Waiting #{limit}hour...")
sleep(Hour * limit)
print("\n\n")

#Acsess
open("http://localhost:4567/#{mode}").read
print("Done\n")

Sinatra にタイマー機能をつける

上記のタイマだとターミナルからしか使えないのですこし面倒 しかもターミナルウインドウを閉じてしまうとプロセスも閉じてしまうので困ってしまう なので Sinatra と同じプロセスで動かして尚且つブラウザからも操作できるようにしてみる

コードはこんな感じになった

  1. '/'でタイマのボタンT(/timer/standOn)を押す
  2. 2つめのパスに記したアクション(例えばstandOn)を引数に/timerが開く
  3. /timer/standOnで時間を入力して時間とアクションをpostで送る
  4. app.rbがpostを受けて別スレッドでsleepさせておき本スレッドではすぐに/にリダイレクトする
  5. 別スレッドが時間になったら任意のアクションのアドレス(例えばlocalhost:4567/standOn) にアクセスする

このように直接時刻(6:30)を指定する方法と数時間後(4時間30分後)を指定する方法を用意した Gyazo

雑感

  1. タイマの時間指定をするフォームでinput type="tell"にするとiPhone等で数字入力するときにテンキーキーボードが出てくるのでカッコイイ
  2. 将来的にはタイマボタンT/timer/standOnに画面遷移するのではなくon/offボタンの長押しで画面遷移したい
  3. iosのブラウザでinput type="time"を表示するとinputの親要素ごと上下に数ピクセルずれてしまったのでなんとかしたい
  4. MaciPhone でアクセスできるとお布団から出なくても部屋の明かりをon,offできて寝る時に便利
  5. 照明のタイマー機能でやすやす起きれると思っていたら、布団に頭まで潜っているので意味がなかった(特に冬は)

コードはGithubとgistにある

  1. Sinatra のコード Coro365/home-control
  2. Arduino のコード It receives the serial signals to control the switches.

次回予告

次回は今回のコードを Raspberry Pi に移します
Raspberry Pi で家電を操作する2 (Raspberry PiにWebサーバを設置して照明を操作する)[未投稿]です

参考ページ

  1. Sinatraについての質問です Sinatraのサーバにlocalhost:4567で… - 人力検索はてな
    • ありがとうございました
  2. sinatraでDNS逆引きを止める | TechRacho
    • DNS逆引きキャンセル
  3. Ruby - rerunでSinatraのファイル変更時にリロード - Qiita
    • rerunについて
  4. inline-blockを並べた場合に発生する「隙間」を消去するCSS » INSPIRE TECH
    • inline-blockの隙間

ありがとうございます