ゆっくり技術ノート!

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

玄関をSuicaで開ける

玄関をSuicaで開ける

この記事は おうちハック Advent Calendar 2015 の23日目の記事です。

動機

私は普段、家の鍵を他の鍵と一緒にでまとめてをオシリポケットに入れているのですが、座ると鍵が当たって痛いので PASMOSuica で開けられるようにしたいと思います

私は iPhone なので FeliCa を搭載していないため iPhoneiPhoneケースの間に PASMO磁気干渉防止シートを挟んで使っています

では、作っていきましょう!

この記事を見れば作れるようにしたいので少しくどくなるかもしれませんが、ご了承ください
セキュリティはあまり考えていません。試す際は自分で対策してください
(でも多分、空き巣は NFC をハックするより窓を割って入ってくる方が簡単)

構成

今回、時間をかけず簡単に作るために機能を最低限の「NFC をかざしたら鍵を開ける(そして自動で閉める)」という機能に絞ることにします
NFC リーダ はこの手の工作では定番の RC-S380 を使います
サーボモータは鍵のサムターン(つまみ)を回すことを考えてトルクの大きなものを秋月電子で購入しました

https://gyazo.com/ef7c2750e751b7af3209cf18c9c2fe93:plain:w300

NFC からidmを取得する

Raspberry Pi の公式から Raspbian jessie を落としてSDカードにセットし IPアドレスの固定等の設定を行ったら(今回はRaspberry Pi Bを使います)
いよいよ PASMO から idm を取得します
idm とは PASMO/Suica 等の製造番号のことです

nfcpy

NFC を使うためのツールであるnfcpyを使います
これは公式サイトGetting started の通りやるとインストールできました
bzrというバージョン管理システムをinstallして、nfcpyをinstallしたい場所に移動、bzrでnfcpyをinstallします

sudo apt-get install bzr
cd <hoge>
bzr branch lp:nfcpy trunk

続いて Python で USB を使うたに python-usb をinstallします

sudo apt-get install python-usb

できたらサンプルコードを動かしてみましょう

sudo python examples/tagtool.py

PASMO等をタッチして、こんな感じにIDが表示されたら成功です https://gyazo.com/450bf973357d0df5bcfa0f59bc1cff4c

今回はこのサンプルコードをそのまま使わせてもらいます

サーボを動かす

ServoBlaster

サーボを動かすにはServoBlasterというCで書かれたツールを使います
git で公開されているのでgit clone してきて、Cのコードをmakeでコンパイルしinstallします

git clone git://github.com/richardghirst/PiBits.git
cd PiBits/ServoBlaster/user
make
sudo make install

サーボを動かしてみる

ServoBlasterは/dev/servoblasterに文字列[Servo_number]=[0-100]%を書き込むことでサーボを動かしてくれます

echo 4=100% > /dev/servoblaster

ServoBlasterで指定するServo_numberはgpioのピン番号とは違うので注意が必要です
https://gyazo.com/0c86c45477643e795ca7b5481fd835b8
https://gyazo.com/00d9e01f2b023184ecb46b7694edd4ae
ここではパーセンテージで指定するのでサーボを見ながら鍵を開けるのに良い角度を探ります
私のサーボの場合は91と40でした
このServoBlasterのいいところは使っていない時サーボに電圧をかけないところです
このため普通の鍵でも手で開閉錠できます

サーボが動いたら NFC をかざした時サーボを回してみましょう

サーボについて追加情報があります *1

登録したidmの時サーボを回す

Ruby から nfcpy と ServoBlaster を使うコードを書きます
ただコマンドを実行するだけなので簡単です
(ServoBlasterは Ruby の File.write ではダメな模様)

#ここは先ほど表示させた自分のidmに書き変える
USERS = {"Bob_Marley" => "xxxxxxxxxxxxxxxx",
        "Alice_Cartelet" => "xxxxxxxxxxxxxxxx"}
#ここも自分のサーボにあったものに書き換える
#s03t-2bbmg servo
UNLOCK_ANGLE = "91"
LOCK_ANGLE = "40"

AUTO_LOCK = 40

def nfc()
    #ここも自分の環境にあったものを指定
    `sudo python ~/Documents/nfc/trunk/examples/tagtool.py`
end

def idm(text)
    m = text.match(/ID=(.*?)\s/)
    idm =m[1]
    print("IDm = #{idm}\n")
    return idm
end

def unlock()
    print("Unlocking\n")
    `echo 4=#{UNLOCK_ANGLE}% > /dev/servoblaster`
end

def lock()
    print("Locking\n")
    `echo 4=#{LOCK_ANGLE}% > /dev/servoblaster`
end

loop do
    idm = idm(nfc)
    unlock_user = USERS.key(idm)

    unless unlock_user == nil
        print("Welcome back #{unlock_user}!\n")
        unlock

        print("Wait #{AUTO_LOCK}sec...")
        sleep(AUTO_LOCK)
        print("\n")

        lock
    else
        print("Illegal user\n")
    end

    print("Please wait reader restart...\n")
end

実行するとこんな感じに動くと思います https://gyazo.com/08b4d3d417724a3c00b491bfa92ea553 動画には写っていませんが40秒後に閉まります

土曜の進捗

A video posted by coro365 (@coro365) on

ハードウェア

鍵のサムターン(つまみ)にサーボを取り付けるための木材をノコギリとドリルで作成します
30mm*30mmの角材を30mmで切ってサイコロ状のものを作成、サムターンに合う穴を開けます
Image from Gyazo

続いてサーボを固定するためにダンボールでマウントを作成します
ダンボールを10枚積層、木工ボンドで合わせて完成です
ダンボールだととても簡単でよかったです(1時間ほどアニメ見ながら切りました)
Image from Gyazo

レーザ加工機が使う余裕があればアクリルで作ってもいいかもしれません
ただダンボールでも木工ボンドで補強するとかなり頑丈になります
ちなみに今回は溜まりに溜まったamazonの箱を使いました
https://gyazo.com/901a037740a05a6df9c20aec7ca3547a

取り付けて完成です

Image from Gyazo 動かすとこんな感じ(このNFCリーダは表からも裏からも使えました)

今後

  • 開閉錠ボタンと状況表示用のLEDを付けたい
  • 鍵が開いているか閉まっているかの状態を確認したい
  • リードスイッチ(磁気を感知するスイッチ)でドアの開閉を検知してオートロックの挙動に生かしたい
  • Sinatraとかでブラウザ上で開閉とログの確認をしたい
  • Supervosorを使ってRaspberry Piのデーモンにする

雑感

  • ServoBlasterとnfcpyが優秀ですぐにできてしまった
  • 一番時間がかかったのはマウントを作成するところ(ダンボールで作る前にアクリルをノコギリとドリルで加工して半日無駄にしている)
  • PASOM決済だけで生活できればiPhoneだけ持ち歩く生活にできるかも
  • 個人が少しのお金で、いろんなことが可能な環境は素晴らしいなと思いました(この方向で人間、進化してもらいたい)
  • 今後も隙を見ておうちを便利にしていきたいです

初めて Advent Calendar に参加しましたがイベントドリブンな感じで作ることができたので参加してよかったです(参加しなかったら作らなかったかも)
そんな おうちハック Advent Calendar 2015 の明日の記事は @YarmUIさん の スマートメータを自作した話 です
良いお年を!

購入したもの

Raspberry piは部屋に転がっていたものを使ったので出費は 4000円ほど

  1. Amazon.co.jp: Raspberry Pi Type B 512MB: パソコン・周辺機器
  2. Amazon.co.jp: SONY 非接触ICカードリーダー/ライター PaSoRi(パソリ) USB対応 RC-S380: パソコン・周辺機器
  3. GWSサーボ S03T/2BBMG/F(フタバ): サーボ 秋月電子通商 電子部品 ネット通販

ドアセンサ、開閉スイッチなどをつける時は必要です

  1. タクトスイッチ 12mm TVGP01-G73BB(黒): パーツ一般 秋月電子通商 電子部品 ネット通販
  2. ピンヘッダ 1×40 (40P): パーツ一般 秋月電子通商 電子部品 ネット通販
  3. ケース入りリードスイッチ(磁石付セット)MC-14AG: センサ一般 秋月電子通商 電子部品 ネット通販

参考ページ

  1. Raspberry PiにRaspbianをインストールする for Mac OSX
  2. Raspberry Pi 2 (Raspbian: jessie) でIPアドレスを固定する
  3. PWMでRaspberry PiのLEDの明るさ調整する
  4. FeliCa(フェリカ)IDmとは?|ステルス・ネットワークス
  5. Python module for near field communication — nfcpy trunk documentation
  6. Raspberry-PiにおけるGPIO関係ツールのインストール方法 | Раздан-3
  7. GPIO: Raspberry Pi Models A and B - Raspberry Pi Documentation
  8. nfcpyでお手軽NFC開発[1/2]

ありがとうございます

追記1 2017/08/09

サーボモータを回転させた際RasPiが再起動してしまう時は、別に電源を用意してください。
私の場合は、以下のように別電源にしました。(この方法が最適解かどうかはわかりません)

  1. USBケーブルを用意し皮膜をはがした後、プラスとマイナスの線をより分けます。
  2. プラスの線はサーボのプラスにつなぎ、マイナスの線 はサーボ と RasPi のGroundに繋ぎます。
  3. USBケーブル自体はApple USB 充電器 に繋ぎます

追記2 2017/08/09

多くの方にこの記事を読んで頂き大変嬉しく思っております。
また、この記事を見て同じものを作ろうとしている方の何人に連絡をいただきとても嬉しいです、記事にした甲斐がありました。
記事公開から1年以上経ち、その間にも iPhoneSuica 対応したり、電子マネー対応の店が増えてきたり、スマフォでATMからお金を引き出せたり、iPhone 1つで生活できる世の中に近づいている気がしてワクワクしています。

さて、この記事で作った物にLED,ドア開閉センサ,ボタンを追加した物がGithubに上がっているので、こちらもどうぞ。
github.com

追記おわり

*1:追記1 2017/08/09 をご覧ください

AppleScriptでiTunesのテレビ番組を連続再生する

iTunes のテレビ番組を連続再生するには再生したいテレビ番組をプレイリストに登録してリピート再生するという方法がある。
しかし、毎度毎度プレイリストを作るのは大変なので AppleScript を使って自動で次のエピソードに移動するようにしてみる

仕組み

  1. Scriptを起動したら TV Show が再生されていないか20秒毎にチェックする
  2. 再生されていたらそのEpisode の合計時間と現在の再生位置から残りの再生時間を3分ごとに調べる
  3. 残りの再生時間が3分以下になったら1分ごとにチェック、1分以下になったら1秒ごとにチェック
  4. 残りの再生時間が1秒以下になったら次のEpisode に移動する
  5. 2にもどる

今回使った命令いろいろ

次のEpisode に移動

  • これは他の種類のメディア(音楽とか)にも使えてキーボードについている送りキー[▶︎▶︎]と同じ機能を持つ
tell application "iTunes"
    next track
end tell

再生中のトラックの合計時間を調べる

  • 1:20:30(h:m:s) のように出力される
tell application "iTunes"
    set timeSize to time of current track as text
end tell

再生中のトラックの再生済みの時間を調べる

  • 秒で出力される
  • 再生されていない時は missing value となる
tell application "iTunes"
    set nowPositionSec to player position
end tell

コードは Gist にある

This applescript will automatically play the episode of the next iTunes TV show. · GitHub

  • 右にあるDownload ZIPからダウンロードする
  • Script Editor で開いて.app形式で保存する
  • Spotlight から起動したり、iTunes Scriptフォルダに入れて iTunes からも起動できる
  • iTunes Scriptフォルダは~/Library/iTunes/Scripts

参考ページ

  1. 鳶嶋工房 / AppleScript / Tips / スクリプトの中断
  2. iTunes for Mac まとめ - AppleScript
    • iTunes関連の命令について
  3. life log: AppleScript 最速基本文法マスター
  4. iTunes Scripts の使い方
    • iTunes Scriptフォルダの場所

ありがとうございます

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の隙間

ありがとうございます

Ruby で Pushbullet を使う

PushbulletAPI を使って各デバイスに通知を送ってみる

Pushbullet

Pushbullet はブラウザ又は Mac/Windows/iOS/Android のアプリからアクセスできて特定のデバイスにテキストや地図、ファイル等を送信できる。送信を受けたデバイスには通知センターに表示できる
今回はこれを使って Mac から任意のタイミングで iPhone にpush通知 を送るようにしたい

Gem インストール

Ruby で扱うための gem が公開されていたのでこれをインストール

gem install washbullet

自分のアクセストークンを Pushbullet の Setting から入手

32桁の文字列がアクセストークンになっている

アクセストークンを使って API から自分のid とデバイスのid を調べる

ターミナルで以下のコマンドを実行する

自分のid

curl -u Your_Token: https://api.pushbullet.com/v2/users/me

バイスのid

curl -u Your_Token: https://api.pushbullet.com/v2/devices

JSON が帰ってる。 "iden":に続く文字列がidになる

取得したidで通知を送る

require 'washbullet'

client = Washbullet::Client.new('Your_Token')

client.push_note('Your_Device_Id', 'title', 'messege')

これを実行するとで送信された

f:id:Coro:20150201222227p:plain

id を Ruby から調べる

手動でidを読むのは面倒なのでアクセストークンを入力すると自動で自分のid と Pushbullet に登録されたデバイスのid を返すようにしてみた

require "json"

def getUserId (token)

    api_response = `curl -s -u #{token}: https://api.pushbullet.com/v2/users/me`
    userId = JSON.parse(api_response)

    return userId["iden"]
end

def getDeviceId (token)

    api_response = `curl -s -u #{token}: https://api.pushbullet.com/v2/devices`
    deviceInfo = JSON.parse(api_response)

    deviceIds = Array.new
    deviceInfo["devices"].size.times do |i|
        deviceId = deviceInfo["devices"][i]["iden"]
        deviceName = deviceInfo["devices"][i]["nickname"]
        deviceModel = deviceInfo["devices"][i]["model"]

        deviceIds.push([deviceId, deviceName, deviceModel])
    end

    return deviceIds
end


token = "Your_Token"

p getUserId (token)
p getDeviceId (token)

実行すると自分のidとデバイスid,デバイスのニックネーム,デバイスモデルが表示される

参考ページ

  1. Pushbullet API Documentation
    • 公式ドキュメント
  2. hrysd/washbullet
    • 今回使ったgem、お世話になります
  3. Ruby 1.9 以降で JSON を扱う - 見上げれば、空

ありがとうございます。