5. 人感センサーを使う – こちらから攻める
この記事はiOSDC2022で発表したセッション、Swiftで我が家を より便利に、安全に!の発表の第4部の内容をまとめたものになります。
この記事は、「4. カードリーダーと連携する – SuicaのIDを読み取る」 の記事の続編です。もしまだ読んでいない方は読んでいただけると!
この記事では、人感センサーの使い方と、SwiftからBashコードの実行方法やSwitchBotのAPIの使い方を紹介します。
人感センサーをトリガーに処理を実行する
今回はラズパイと連携した人感センサーが作動して、家主が在宅中でない(①緯度経度や高度情報が家と一致しない、②重量センサーに物が無い、③Suicaで認証されていない)場合のみ実行する、↓を実行することを目指します
- スピーカーで警告音を流す
- SwitchBotでデバイスを動かす
- Push通知を送る(発表では省略)
- カメラで侵入者を撮影し、Twitterに画像を投稿(今後実装予定)
今回使用した人感センサーは、↓のものです!
実装イメージは以下の通りです!
人感センサーの連携の実装
まずは回路を作りましょう!
今回は電源2本、通信用1本(オレンジ/GPIO25)の3本を使用しています
また人を検知した時に、LEDが点灯するようにしています!
次にコードを確認しましょう!
最初はいつも通り、GPIOインスタンスのセットアップです!
回路の箇所で説明した通り、人感センサー用とLED用のGPIOをセットアップしてます。
let humanSensorGpio = gpios[.P25]!
let humanSensorLedGpio = gpios[.P16]!
humanSensorGpio.direction = .IN
humanSensorLedGpio.direction = .OUT
humanSensorLedGpio.value = 0
次にGPIOの値を監視しましょう
SwiftyGPIOには、GPIOのvalueの変化を監視するメソッドがあり、今回はそのメソッドを使用しますonChange
メソッドを使うことで、valueが1<->0に変化した時に、クロージャが呼ばれるので、その中でLEDの点灯/消灯を設定し、その変化を通知する処理を実装します
humanSensorGpio.onChange { gpio in // ①Called when value changes
if gpio.value == 1 {
// Human detected
self.humanSensorLedGpio.value = 1 // ②turn on the LED
// (Omit: notify change)
} else {
self.humanSensorLedGpio.value = 0 //③turn off the LED
// (Omit: notify change)
}
}
最後に上記の処理を使ったメソッドを実装しました。didDetected
のBoolの値をクロージャーで受け取ります
そしてdidDetected
がtrueの時に、処理が実行できるようにしています。
// ①methods for monitoring
GpioManager.shared.observeHumanMoved { didDetected in
if didDetected {
print("Human detected!")
// (Omit: Execute processing when a person is detected)
} else {
print("Human Not detected!")
}
}
準備ができたら、試しに動かしてみましょう!
ちゃんと人感センサーがちゃんと動いて、人の動作を検知できていることが確認できました!🎉
警告音を再生する実装の詳細
次に侵入者に対して警告音を再生する機能の実装です!
今回はサンプル用のmp3を再生するのに、mpg123のコマンドを使用します
Swiftから上記のサンプル音声を再生するShellスクリプトを実行するようなイメージです!
実行すると、ラズパイのイヤホンジャックに接続したスピーカーから警告音声が流れるようにします!
まずはSwiftからShellスクリプトを実行する共通処理を実装します(参考)
@discardableResult
func shell(_ args: String...) throws -> Int32 {
let task = Process()
task.executableURL = URL(fileURLWithPath: "/usr/bin/env")
task.arguments = args
try task.run()
task.waitUntilExit()
return task.terminationStatus
}
Macの場合、Package.swiftにmp3をリソースとして指定する必要があるので、追加します
resources: [
// Copy resource file to bundle
// Mac: Build artifacts are generated in the DerivedData directory
// Linux: You can directly refer to the audio files in the cloned directory
.process("warning_voice.mp3"),
]
上記に少しコメントで記載がありますが、MacとLinuxだとコマンドのパスやビルドされた成果物のmp3のパスが異なるので、それぞれ分けて設定します。
Macの場合、実行時のmp3のパスは、DerivedDataのパスになり、Linuxの場合はリポジトリをcloneしたパスになります。
let command: String
let path: String
#if os(Linux)
command = "/usr/bin/mpg123"
path = "\(Path.current)/Sources/SwiftHomePi" // If linux, returns repository path
#else
command = "/opt/homebrew/bin/mpg123"
// If Mac, returns DerivedData path
path = "\(Path.current)/SwiftHomePi_SwiftHomePi.bundle/Contents/Resources"
#endif
最後に上記で実装したShell実行用の共通メソッドに、用意したコマンドとmp3のパスを設定します
コマンドとパラメーターはそれぞれ別々のStringとして設定する必要があります
let voiceFilePath = "\(path)/warning_voice.mp3"
do {
// ①Separate commands and parameters
try shell(command, voiceFilePath)
} catch {
print(error)
}
SwitchBotのデバイスを動かす実装詳細
次にSwitchBotのデバイスを動かす実装です!
今回は我が家にある電源タップとテープライトを操作します!
電源プラグは、ネットワーク経由で電源のON/OFFを操作できて、テープライトは、ネットワーク経由でライトのON/OFF、色の種類を操作できます!
SwitchBotのAPIの使い方については、別のブログにまとめました!
詳細はこちらを確認いただけると!!
今回は電源タップには、IKEAで買ったスタンド照明を繋いで、玄関ラックに置きます。
人感センサーで検知した時に、スタンドの60W(すごい明るい)電球をつけることで、侵入者の視界を奪います!
60Wのライトは、かなり眩しくて、突然点灯したら、確実に視界が持っていかれます!笑
ちなみにこのシステムを勝手に「太陽拳システム」と呼んでいます!笑
テープライトは、赤白に点滅させて、侵入者を警告します!
デモ
上記で実装した物をまとめて動かしてみましょう!
今回はデモなので、机の上にラズパイと人感センサーを設置して、手をかざした時に、
①電源タップ経由でスタンドのライト点灯、②テープライト点灯、③警告音再生
の順に処理を実行していきます!
なお↓の動画はSwitchBotの屋内カメラを玄関に無理やりくくりつけて、撮影しています!笑
普段監視用に玄関に置いているカメラが役に立ちました笑
無事想定通りに動かすことができました!!🎉🎉🎉
Push通知を送信する処理
今回は簡単に実装するため、FirebaseのFCMのLegacyAPIを使用します。
SwiftHomePiに以下の実装を追加して、Firebase経由でPush通知を送信できるようにします。
デモなので、Push通知の送付先のFCM Token
は手動で設定するようにしています。
本実装、及びアプリ側の実装は、Firebaseのドキュメントを確認いただけると!(コード)
let serverKey = SwiftHomeCredentials.fcmServerKey
// This is demo app, so fmc token must be set manually.
let fmcToken = "xxxxxxxx"
let headers: Alamofire.HTTPHeaders = [
.accept("*/*"),
.contentType("application/json"),
.acceptCharset("utf8"),
.authorization("key=\(serverKey)")
]
let parameter = [
"registration_ids": [
fmcToken
],
"notification": [
"title": "侵入者通知⚠️",
"body": "家への侵入者を検知しました!カメラを確認してください!",
"badge": "0",
]
] as [String : Any]
let urlString = "https://fcm.googleapis.com/fcm/send"
AF.request(urlString,
method: .post,
parameters: parameter,
encoding: JSONEncoding.default,
headers: headers
)
.responseString(completionHandler: { response in
print(response)
})
うまくいくと、↓のように、通知が飛びます!
まとめ
これで5部の紹介は終わりです
4部までに実装したことも使いつつ、デモも交えて紹介することができました!
太陽拳とかふざけつつも、実際に導入できそうな実装ができたのは良かったと思いました!
次はこの発表のまとめを書いた記事です