Slide 1

Slide 1 text

1 すぎー/@u5_03 #iosdc 2022/09/10 17:15〜 Track C Swiftで我が家を より便利に、安全に

Slide 2

Slide 2 text

2 0. 導入 すぎー/杉山 優悟 " ! @u5_0! iOSDC初登壇です!登壇用に、Macを新調m 身長は188cm(よく大きいと驚かれるd 前職(2018~)から、iOSエンジニア。元々中高の社会科教員志I 趣味: 美味しいものを食べる、料理、アニメ/漫画、ガジェット収† 最近買った物: Mac(M1 Pro)、ハンモック(ハンモックはよいぞ!) README

Slide 3

Slide 3 text

3 0. 導入 ラズパイやHerokuでのSwiftの実装についての発P 53 既存の 機能を強化す! 43 我が家の状態を監視す! @3 経由でiPhone、 を連携させる スマートホーム Heroku ラズパイ このセッションの内容

Slide 4

Slide 4 text

4 0. 導入 ラズパイやHerokuでのSwiftの実装についての発F R 既存の 機能を強化す! R 我が家の状態を監視す! R Heroku経由でiPhone、ラズパイを連携させる スマートホーム このセッションの内容 Project SwiftHome と命名

Slide 5

Slide 5 text

5 0. 導入 sw スマートホームとは„ uw ネットワーク経由で家電を操作できるようにすU †w GoogleHomeでリビングの照明をつけるなq )w ラズパイとは„ uw が正式名! ‡w 小さなシングルボードコンピューター bw OSを書き込んだSDカードを入れて使う Raspberry Pi 用語解説①

Slide 6

Slide 6 text

6 0. 導入 sw スマートホームとは„ uw ネットワーク経由で家電を操作できるようにすU †w GoogleHomeでリビングの照明をつけるなq )w ラズパイとは„ uw が正式名! ‡w 小さなシングルボードコンピューター bw OSを書き込んだSDカードを入れて使う Raspberry Pi 用語解説① 要は。。
 はよいぞ! という発表です!笑 スマートホーム

Slide 7

Slide 7 text

7 0. 導入 ˆQ Herokuとはt AQ アプリケーションを実行するために必要なプラットフォームを提 供するサービス(PaaS8 'Q 便利で使いやすいレンタルサーバーのようなイメー wQ 今回は (ServerSideSwiftのフレームワークの1つ)で 実装したサーバーをデプロイするのに利用 Vapor 用語解説②

Slide 8

Slide 8 text

8 0. 導入 ○話すこ „y ラズパイやHerokuでのSwiftやVaporの実C ‚y 実現する機能の中で、コアな実装の手順(詳細はブログに記載% $y 実装でつまづいた点や個人的に試行錯誤した方法 ×話さないこ „y ラズパイのセットアップ方t ‚y 主にハード周りの技術要素の詳細や原理 このセッションで話すこと、話さないこと

Slide 9

Slide 9 text

9 0. 導入 …„ 発表者は、 でe a„ 発表情報は不正確、もしくは情報が不足している場合がありまe F„ 今回実装しているコードやリポジトリは、個人開発なので、アーキテ クチャやセキュリティが適当考慮できてない箇所が多くありまe „ のため、コードや情報を全て 発表/記載できないので、 を確認してくださI a„ 気になる箇所は個別に確認してください( ) ハード周りの技術に精通しているわけではない 発表の時間やスライドのスペースの制約 詳細はブログ記事(後述) かなり飛ばします! 注意事項 ️

Slide 10

Slide 10 text

Use Load cell weighing sensor Use from Google Home Deploy on Play warning sound Communicate through GPIO Communicate using Vapor SwiftHome Read Suica ID using card reader Use iPhone Sensors Collaborate with Home appliance Run Bash from Swift Run Python from Swift Run Swift on Raspberry Pi

Slide 11

Slide 11 text

11 0. 導入 ③SwiftHomePi ①SwiftHomeApp Postgress Vapor ②SwiftHomeServer Vapor Rest API Web Socket セキュリティを考慮して、 直接通信はしない SwiftHome構成

Slide 12

Slide 12 text

12 0. 導入 ①SwiftHomeApR IG 位置情報、 をHerokuに送9 HG をスキャンし、Herokuに送9 5G ユーザーが家にいるかの判断材料にするため ②SwiftHomeServe IG を実装して、データの仲) HG にデータを保存 標高データ SuicaのID API/WebSocket Postgres SwiftHome実装①

Slide 13

Slide 13 text

13 0. 導入 ③SwiftHomePW DB を使用したLEDの操作、センサーのデータ読み取S CB の実 VB のAPI実行(家電の操作を行う ÉB Swiftから のコードの実行 ラズパイのGPIO Swift/Vapor SwitchBot Python/Bash SwiftHome実装②

Slide 14

Slide 14 text

14 0. 導入 ・ GPIOとは? 1. ‘General Purpose Input/Output’の 汎用I/Oポートの意味 2. →のように電源、GND(マイナス)、I/ Oなどポートごとに役割が分かれている 3. このピンにLEDやセンサーなどを繋げ て、回路を作ることでそれらを操作できる 用語解説③ https://deviceplus.jp/raspberrypi/raspberrypi- gpio/

Slide 15

Slide 15 text

15 0. 導入 玄関ラックに人感センサーを取り付け、侵入を通知&侵入者へ警告する 不審者侵入! 人を検知 ①スピーカーから警告音再生 ③Push通知の送信 ②SwitchBotの デバイス操作 if not 在宅中 人感センサー 通知 この発表で最終的に実装するもの

Slide 16

Slide 16 text

16 0. 導入 今回の発表は1~5の5部構成です 0. 導入 - 発表やSwiftHomeの概要紹q ˜ˆ ラズパイでSwiftを動かす - GPIOを使って、LEDを点滅させ… 1ˆ Vaporを動かす - iPhoneとラズパイ間をVaporで通a "ˆ ラズパイでセンサーの値を読み取る - 重量センサーの値を取得す… fˆ カードリーダーと連携する - SuicaのIDを読み取… dˆ 人感センサーを使う - こちらから攻める 目次

Slide 17

Slide 17 text

17 0. 導入 1.ラズパイでSwiftを動かす - GPIOを使って、LEDを点滅させる 2.Vaporを動かす - iPhoneとラズパイ間をVaporで通信 3.ラズパイでセンサーの値を読み取る - 重量センサーの値を取得する 4.カードリーダーと連携する - SuicaのIDを読み取る 5.人感センサーを使う - こちらから攻める 目次 ①ラズパイでのSwift、Vaporの基本実装の紹介 ②家主が在宅中かどうかのチェック機能を実装の紹介 ③不審者が侵入した時の実装の紹介

Slide 18

Slide 18 text

18 1. ラズパイでSwiftを動かす - GPIOを使って、LEDを点滅させる - Run Swift on Raspberry Pi Communicate through GPIO

Slide 19

Slide 19 text

19 ラズパイでSwiftを動かす LED点滅(通称Lチカ)は、電子工作の’Hello world’みたいなもV „ SwiftでGPIOを操作するには、 を使C a Ref: @ 今回は↓のような回路を作成すI a ラズパイのGPIO16ピン(プラス) -> LEDのプラス(長い方f & LEDのマイナス(短い方) -> 抵 S 抵抗 -> ラズパイのGPIOのGND(マイナス)ピン SwiftyGPIO https://github.com/uraimo/SwiftyGPIt SwiftyGPIOでLEDを点滅させる

Slide 20

Slide 20 text

20 ラズパイでSwiftを動かす ブレッドボードとはY 4 電子回路を半田付けなしに、簡単に作るための基3 g LEDなどを部品を配置でき6 0 以下の % 溝を隔てて縦1W %% 上下の2列は横一W 穴が内部で繋がってい6 用語解説④

Slide 21

Slide 21 text

21 ラズパイでSwiftを動かす ブレッドボードと回路図 GNP GPIO16

Slide 22

Slide 22 text

22 ラズパイでSwiftを動かす ラズパイの開発環境 $ cat /proc/device-tree/model > Raspberry Pi 4 Model B Rev 1.2 $ cat /etc/os-release > PRETTY_NAME="Ubuntu 22.04 LTS" > NAME="Ubuntu" > VERSION_ID="22.04" > VERSION="22.04 (Jammy Jellyfish)" > VERSION_CODENAME=jammy $ swift --version > Swift version 5.6.1 (swift-5.6.1-RELEASE) > Target: aarch64-unknown-linux-gnu # ラズパイのハード情報 # ラズパイで使用しているOSの情報

Slide 23

Slide 23 text

23 ラズパイでSwiftを動かす ラズパイに のOSをインストールしているのはなぜa Y 今回は を使う想定だが、標準的に使われている だと、 の設定やビルドが通らなƒ w 参考サイトでも、同じ理由で を使用する例が多かっX 9 執筆時点では でSwift を使用できるが、 だと、Swift までしか利用できなƒ " 開発のしやすさも考慮して、 で進めることにしました Ubuntu Vapor Raspberry Pi OS Vapor Ubuntu Ubuntu 5.6 Raspberry Pi OS 5.1.5 Ubuntu ラズパイの開発環境

Slide 24

Slide 24 text

24 ラズパイでSwiftを動かす # Step 1 - System update and install curl # Step 2 - Run the quick install script # Step 3 - Choose which version of Swift to install # Choose swift version # Step 4 - Install Swift # Ref: $ sudo apt update sudo apt upgrade $ sudo apt install curl $ curl -s https://archive.swiftlang.xyz/install.sh sudo bash $ sudo apt install swiftlang $ swift --version && | https://swift-arm.com/installSwift/ ラズパイにSwiftをインストールする

Slide 25

Slide 25 text

25 ラズパイでSwiftを動かす 今回は というパッケージを作成する SwiftHomeLED $ mkdir SwiftHomeLED $ cd SwiftHomeLED $ swift package init --name SwiftHomeLED --type executable サンプルプロジェクトを作成する

Slide 26

Slide 26 text

26 ラズパイでSwiftを動かす Package.swiftに のdependenciesを設定する SwiftyGPIO Package.swiftにライブラリを追加する // (省略) // (省略) name: , dependencies: [ . (url: , from: ), ], targets: [ dependencies: [ ]), "SwiftHomeLED" "https://github.com/uraimo/SwiftyGPIO.git" "1.0.0" "SwiftyGPIO" package Package.swift

Slide 27

Slide 27 text

27 ラズパイでSwiftを動かす 0.2秒おきに 処理を実装する LEDを点滅させる 1 2 3 4 5 6 7 8 9 10 import import let = let = ! = while = == ? : gpios SwiftyGPIO. ( : .RaspberryPi4) ledGpio gpios[.P16] ledGpio.direction .OUT ( ) { Thread. ( : ) ledGpio. ledGpio. } Foundation SwiftyGPIO GPIOs for true sleep forTimeInterval 0.2 value value 0 1 0 // ①ラズパイ4用のgpioのインスタンス // ②GPIO16ピンを操作するためのインスタンス // ③今回は電流を流すので、.INではなく、.OUTにする // ④GPIO16の値を0.2秒ごとに更新 // ⑤1ならLEDが点灯、0なら消える main.swift main.swiftを編集する

Slide 28

Slide 28 text

28 ラズパイでSwiftを動かす ラズパイとブレッドボードを繋げて、 を実行する SwiftHomeLED $ swift run SwiftHomeLEDを実行する

Slide 29

Slide 29 text

29 ラズパイでSwiftを動かす

Slide 30

Slide 30 text

30 ラズパイでSwiftを動かす リポジトリ https://github.com/u5-03/SwiftHomeLED

Slide 31

Slide 31 text

31 2. Vaporを動かす - iPhoneとラズパイ間をVaporで通信 - Communicate using Vapor Deploy on Use iPhone Sensors

Slide 32

Slide 32 text

32 2. Vaporを動かす g— とはf d— ServerSideSwiftのフレームワークの1r — フロントとサーバー側をSwiftで書くことで、Codableのレス ポンスやモデルなどのコードを共通化できるメリットがあ v— DBやWebSocketも簡単に利用でき '— 執筆時はMacとLinuxで利用可 s— ( ) Vapor ドキュメントが充実している https://vapor.codes 用語解説⑤

Slide 33

Slide 33 text

33 2. Vaporを動かす ③SwiftHomePi ①SwiftHomeApp Postgress Vapor ②SwiftHomeServer Vapor Rest API Web Socket e 通信内d ˜f iPhoneの位置・高度情y xf SuicaのI €f 各種フラグ SwiftHome構成(再掲)

Slide 34

Slide 34 text

34 2. Vaporを動かす ”… というVaporのプロジェクトをMacで作` ‚… API, WebSocketなどを実装して、動作確I q… に をデプロイすB ˆ… , から に接 続して、動作確認 ※ SwiftHomeでは、パスワード、Tokenやエンドポイントなどは、 privateリポジトリの を参照しています SwiftHomeServer Heroku SwiftHomeServer SwiftHomeApp SwiftHomePi SwiftHomeServer SwiftHomeCredentials Vapor実装の流れ

Slide 35

Slide 35 text

35 2. Vaporを動かす ` MacBook Pro(14インチ、2021、Apple M1 ProD ` macO6 ` Monterey 12.2.P ` Xcode 13.4.P ` Vapor4 (執筆時の最新版" ` toolbox: 18.5.P ` Vaporで利用するCLIツール 開発環境

Slide 36

Slide 36 text

36 2. Vaporを動かす MacでVaporをインストールし、プロジェクトを作成する $ brew install vapor $ vapor --help $ vapor new SwiftHomeServer > Would you like to use Fluent? (--fluent/--no-fluent)  > Which database would you like to use? (--fluent.db)  > Would you like to use Leaf? (--leaf/--no-leaf) # ① Select using Fluent or not. (ORM framework for Swift.) #→ y # ②Select database(If using in Heroku, ‘Postgres’ is required.) #→ y # ③Select using Leaf or not.(a powerful templating language with Swift-inspired syntax.) #→ y or n(this time, n) Vaporのセットアップ

Slide 37

Slide 37 text

37 2. Vaporを動かす 生成されたVaporのプロジェクトをビルドし、アクセスしてみる $ cd SwiftHomeServer $ swift run # Access to ‘http://localhost:8080/hello’ ビルド後に、上記にブラウザなどでアクセスすると、 と表示される! Ref: Hello, world! https://docs.vapor.codes/getting-started/hello-world/ Vaporの動作確認

Slide 38

Slide 38 text

38 2. Vaporを動かす デフォルトプロジェクトの を確認する ルーティングの処理 1 2 3 4 5 6 7 8 9 10 // (省略) // ①エンドポイントの指定がない場合に呼ばれる // ②async/awaitも使用することができる // ③ここでエンドポイントとメソッドタイプを指定 // (省略) func throws in -> in ( app: Application) { app.get { req async } app. ( ) { req async } routes _ "It works!" "hello" "Hello, world!" get String routes.swift Vaporのコードを見てみる

Slide 39

Slide 39 text

39 2. Vaporを動かす ③SwiftHomePi ①SwiftHomeApp Postgress Vapor ②SwiftHomeServer Vapor Rest API Web Socket  通信内† „‘ ˆ‘ SuicaのI€ v‘ 各種フラグ iPhoneの位置・高度情– SwiftHome構成(再掲)

Slide 40

Slide 40 text

40 2. Vaporを動かす iPhoneから位置情報と高度情報を を実装する PostするAPI 1 2 3 4 5 6 7 8 9 10 // protectedはBasic認証を適用する実装(詳細はブログに記載) // ①postを設定することで、PostのAPIが実装できる // ②DBモデルへ // (省略: Postgresにデータを保存する処理) // ③asJsonStringはEncodableをjsonに変換 // ④ラズパイへデータ送信(詳細は後述) // ⑤Errorがthrowされなければ、okを返す protected. (. (EndPointKind.deviceInfo.endPoint)) { req async HTTPResponseStatus response req.content. (DeviceInfoModel. ).asDBModel jsonString response. () deviceInfoWebSocket . (jsonString) HTTPStatus.ok } post constant decode asJsonString send throws -> in let = try self let = try try await ? return routes.swift VaporのPostのAPIを実装する

Slide 41

Slide 41 text

41 2. Vaporを動かす ③SwiftHomePi ①SwiftHomeApp Postgress Vapor ②SwiftHomeServer Vapor Rest API Web Socket f 通信内e ™g iPhoneの位置・高度情€ yg SuicaのI‚ g 各種フラグ SwiftHome構成(再掲)

Slide 42

Slide 42 text

42 2. Vaporを動かす SwiftHomeServerでのWebSocketの実装 1 2 3 4 5 6 7 8 9 10 var ? in = in try await ? deviceInfoWebSocket: WebSocket protected. (. (EndPointKind.deviceInfo.webSocketEndPoint)) { , ws deviceInfoWebSocket ws ws.onText { ws, text (text) } } deviceInfoWebSocket . (jsonString) // (省略) // ①WebSocketインスタンスの保持 // (省略) // ②PostされたデータをWebSocketで送信 webSocket constant _ print send routes.swift SwiftHomeでのWebSocketの実装①

Slide 43

Slide 43 text

43 2. Vaporを動かす SwiftHomePiでのWebSocketの実装(Serverとの接続) 1 2 3 4 5 6 7 8 private var ? = weak in guard let = else return = deviceInfoWebSocket: WebSocket WebSocket. ( : , : headers, : eventLoopGroup) { [ ] ws { } .deviceInfoWebSocket ws } // (省略) // ①headerはBasic認証用のheader情報を設定(詳細はブログ) // ②接続後に、WebSocketインスタンスを保持する // (省略: データ保存処理) _ connect to headers on self self self self "\(WebSoketURL)" WebSocketManager.swift SwiftHomeでのWebSocketの実装②

Slide 44

Slide 44 text

44 2. Vaporを動かす 時間の関係で、発表では省略 Herokuセットアップの流れ

Slide 45

Slide 45 text

45 2. Vaporを動かす vi デプロイが完了したら、試しに/helloアクセスしてみC 8i https:/{Herokuのアプリ名}.herokuapp.com/hellF Qi ↓はVSCodeの という拡張機能のキャプチャ Thunder Client デプロイしたアプリにアクセスしてみる

Slide 46

Slide 46 text

46 2. Vaporを動かす

Slide 47

Slide 47 text

47 2. Vaporを動かす ‚w APIのパラメータで送信する以下のセンサーデータを取得すe Pw デバイスの緯度・経度情報(この発表では実装省略( Aw デバイスの絶対高度(m)情4 %w 上記のデータを定期的にHerokuへPostする iPhoneからセンサーのデータを送信する

Slide 48

Slide 48 text

48 2. Vaporを動かす ‡’ にある というclassを使うと、高度情 報を取得でき… e’ 今回は絶対高度と相対高度を取得する(Postするのは絶対高度のみd h’ : 海抜から何メートル8 2’ : データ取得開始時から何メートル変化した8 "’ 利用するには、Info.plistに↓の設定追加が必7 h’ ‘ ’ CoreMotion CMAltimeter 絶対高度 相対高度 Privacy - Motion Usage Description CoreMotionを使って、高度を取得する①

Slide 49

Slide 49 text

49 2. Vaporを動かす SwiftHomeAppの を取得するコードを抜粋して、記載します 絶対高度 1 2 3 4 5 6 7 8 9 10 import private let = if in if != return ! altimeterMeter () CMAltimeter. () { altimeterMeter. ( : OperationQueue.main) { data, error error { } (data .altitude) } } CoreMotion CMAltimeter isAbsoluteAltitudeAvailable startAbsoluteAltitudeUpdates to nil print // ①インスタンスの用意 // ②CMAltimeterが使用できるかの確認 // ③一定タイミングで呼ばれ、データを取得できる // 25.007457757686876868(e.g, 単位はm) AltimeterManager.swift CoreMotionを使って、高度を取得する②

Slide 50

Slide 50 text

50 2. Vaporを動かす G 実機で をprintしてみ0 G MacとiPhoneを持って、屈伸みたいなことしてます笑 相対高度 SwiftHomeAppのキャプチャ 個人情報なので、マスクしてます→ CoreMotionを使って、高度を取得する③

Slide 51

Slide 51 text

51 2. Vaporを動かす `V HerokuにPostするパラメータを用意す9 HV ModelはSwiftHomeServerでも共通で使用した SwiftHomeCoreのDeviceInfoModelを使用する 1 2 3 4 5 6 // ①共通ModuleのSwiftHomeCoreに定義したModelを使用 // ②Dictionaryに変換するextension let = ! let = try! model ( : UIDevice.current.identifierForVendor .uuidString, : userCoordinate.latitude, : userCoordinate.longitude, : absoluteAltimeterValue) parameters model. ( : encoder) DeviceInfoModel deviceId deviceLatitude deviceLongitude absoluteAltimeter asDictionary using DeviceInfoViewModel アプリの情報をHerokuに送信する①

Slide 52

Slide 52 text

52 2. Vaporを動かす Alamofireを使用して、データをHeroku経由でラズパイにPostする 1 2 3 4 5 6 7 8 9 AF. (urlString, : .post, : parameters, : headers) . ( : Empty. , : [ ]) { response response.result { .success ( ) } } request method parameters headers responseDecodable of emptyResponseCodes 200 print // ①DeviceInfoModelのパラメータを設定 // ②WebSocketのheaderと同じ設定 // (省略: エラー処理) self in switch case : "Success" DeviceInfoViewModel アプリの情報をHerokuに送信する②

Slide 53

Slide 53 text

53 2. Vaporを動かす Herokuの というデータを確認する機能を使用して、 を実行し、保存されていることを確認!↓ Dataclips postgresql HerokuでPostされたデータを確認する

Slide 54

Slide 54 text

54 2. Vaporを動かす

Slide 55

Slide 55 text

55 2. Vaporを動かす ‚ SwiftHomePiへ送られたデータと事前にラズパイに設定したデー タを元に、緯度/経度/標高から を追 加 →ここまでスライド50枚超えて、力尽きたので、発表時間を考慮して、 残りの詳細は を確認いただけると! 家にいるかどうかを判定する処理 ブログとリポジトリ Vaporの残りの実装

Slide 56

Slide 56 text

56 3. ラズパイでセンサーの値を読み取る - 重量センサーの値を取得する - Collaborate with Home appliance load cell weighing sensor Communicate through GPIO

Slide 57

Slide 57 text

57 3. ラズパイでセンサーの値を読み取る ‚i ラズパイと を連携させ、センサーの上に物が載っている かどうかを判定すx i 今回は外出時に持ち出すキーケースを置いて利用すx )i 外出中or在宅中を判定すx di Ref: ← 重量センサー https://amzn.to/3QK7Mfl 重量センサーの値を取得する

Slide 58

Slide 58 text

58 3. ラズパイでセンサーの値を読み取る A: status = 家にいる! B: status = 外出中! キーケース 重量センサーで実現したいこと

Slide 59

Slide 59 text

59 3. ラズパイでセンサーの値を読み取る i 家にいるかどうかの判断基準の1つにすH Ti 我が家は ⇩を使っていて、自動施錠で鍵 が閉まることをトリガーに家の照明などをOFFにする設定をしていH fi しかしゴミ捨てや配達受け取り時の施錠でも起動してしまう..i 0i した— )i この3部ではSwiftを使った重量センサーとの連携を紹介 SwitchBot スマートロック 外出時のキーケースを持ち出す時だけ起動 重量センサーの実装によってできること

Slide 60

Slide 60 text

60 3. ラズパイでセンサーの値を読み取る ‡ SwitchBotとは€ ‡ 用のデバイスを販売する会社、プラットフォーC ‡ 電球、プラグ、カーテン、加湿器、掃除機、開閉センサー、玄関 ロック、テープライトなどをネットワークから確認・操作できる スマートホーム ←電源プラグ テープライト→ ←屋内カメラ 玄関ロック→ 後ほど一部出てきます↑ Ref: https://www.switchbot.jp 用語解説⑥

Slide 61

Slide 61 text

61 3. ラズパイでセンサーの値を読み取る 施錠 ① 通知 ② 家電操作 ③ 通知 Webhookの連携 現在の構成(Before)

Slide 62

Slide 62 text

62 3. ラズパイでセンサーの値を読み取る 外出中 or 在宅 更新 If 外出中 通知 施錠 通知 通知 Webhookで連携 ⑤ 家電操作 通知 ① ③ ④ ②SwiftHomeServer 今回の実装イメージ(After)

Slide 63

Slide 63 text

63 3. ラズパイでセンサーの値を読み取る ˜‚ とは?( † „‚ トリガーとイベントをレシピとして登録して、 できるサービ ˆ‚ GoogleHomeで話す→Merossのライトを消b ˆˆ‚ 指定時刻になる→SwitchBotでカーテンをあけu ˆˆˆ‚ 家に近づく→NatureRemoでエアコンをつける IFTTT 様々な サービス同士を連携 https://ifttt.com 用語解説⑦

Slide 64

Slide 64 text

64 3. ラズパイでセンサーの値を読み取る – 今回の重量センサーは、24ビットの信号を出力する という ADコンバーターを使用しています… V– Reference guilde: ‘– データは、 という形式で取得する必要があ‚ V– 通常は、GPIOピンからは0 or 1の値しか取得できなd 1– GPIO5(データ取得用), GPIO6(クロック信号用)を使用すること で、重量のアナログ情報を の単位で取得できる HX711 SPI 24bit https://cdn.sparkfun.com/datasheets/Sensors/ForceFlex/ hx711_english.pdŽ 重量センサーのデータを読み取る①

Slide 65

Slide 65 text

65 3. ラズパイでセンサーの値を読み取る GPIOのインスタンスを用意する 1 2 3 4 5 6 7 let = let = ! let = ! = = gpios SwiftyGPIO. ( : .RaspberryPi4) dataGpio gpios[.P5] sckGpio gpios[.P6] dataGpio.direction .IN sckGpio.direction .OUT GPIOs for // Load sensor // serial clock GpioManager.swift 重量センサーのデータを読み取る②

Slide 66

Slide 66 text

66 3. ラズパイでセンサーの値を読み取る センサーから24bitのデータを取得する 1 2 3 4 5 6 7 8 9 10 sckGpio. dataIn: { sckGpio. dataIn (dataIn ) (dataGpio. ) sckGpio. } maxValue: minValue: value 0 UInt32 0 _ 0 24 value 1 1 UInt32 value value 0 UInt32 16777215 UInt32 8388608 = var = for in ..< = = << | = let = let = // ①24bitのデータを1bitずつ取得 // ②左ビットシフト→新しいbitをORで反映 // 0x7fffff(16777215) is Max value, 0x800000(8388608) is Min value // ③HX711のリファレンス記載の最大データ // ④HX711のリファレンス記載の最小データ GpioManager.swift 重量センサーのデータを読み取る③

Slide 67

Slide 67 text

67 3. ラズパイでセンサーの値を読み取る 校正した重量の値を返す 1 2 3 4 5 6 7 8 9 10 if == || == return var = if & == = ^ + else = return - / (dataIn maxValue dataIn minValue) { } signedData: ( (dataIn minValue) ) { signedData (((dataIn maxValue) )) } { signedData (dataIn) } ( (signedData) defaultLoadValue) refernceUnit 0 Int32 0 Int32 1 Int32 1 Int32 Double // ②補数の処理を実行?(この辺り理解が正確でないです。。) // ②元々の初期値を引き、キャリブレーション(校正)する値(refernceUnit)で処理した値を返す GpioManager.swift 重量センサーのデータを読み取る④

Slide 68

Slide 68 text

68 3. ラズパイでセンサーの値を読み取る d” キャリブレーション(校正)とは˜ U” 正確な値を測るため、 するこb y” →のように重りを使い、複数の計測データの平均値を元に計算す る(本当は分銅などがいいが、無いので硬貨で代用ƒ (” 今回はrefernceUnitを校正値として利q "” 計算したら、 センサー個体ごとに誤差を調整 refernceUnit = 465.375 用語解説⑧

Slide 69

Slide 69 text

69 3. ラズパイでセンサーの値を読み取る 重量センサーから物体が取り除かれたことを監視 1 2 3 4 5 6 7 8 9 10 func -> while if > return break ( : (() )) { ( ) { ( ) () { } () observeLoadPlaced {} } } observeLoadRemoved completion Void true sleep 1 readWeight 30 completion // ①1秒ごとに確認 // ②30gより重い場合は、そのまま // ③(省略: DBのステータス変更処理 / LEDの消灯処理) // ④物体が取り除かれたら、次は物体が置かれることを監視する GpioManager.swift 重量センサーのデータを読み取る⑤

Slide 70

Slide 70 text

70 3. ラズパイでセンサーの値を読み取る Wb 回路の設定は↓の通り( を使っているの一部異なります3 2b コードの色は回路図と写真で同じで5 Rb 電源2本、通信用2本の合計4本を使用しています 拡張ボード 重量センサーのデータを読み取る⑥

Slide 71

Slide 71 text

71 3. ラズパイでセンサーの値を読み取る B この設定と処理を動かせば、↓のように物を置いたかどうかを検知す ることができます 重量センサーのデータを読み取る⑦

Slide 72

Slide 72 text

72 3. ラズパイでセンサーの値を読み取る

Slide 73

Slide 73 text

73 今回の発表は1~5の5部構成です 0. 導入 - 発表やSwiftHomeの概要紹f “ƒ ラズパイでSwiftを動かす - GPIOを使って、LEDを点滅させ€ &ƒ Vaporを動かす - iPhoneとラズパイ間をVaporで通V ƒ ラズパイでセンサーの値を読み取る - 重量センサーの値を取得す€ aƒ カードリーダーと連携する - SuicaのIDを読み取€ •ƒ 人感センサーを使う - こちらから攻める 目次

Slide 74

Slide 74 text

74 出典:ジョジョの奇妙な冒険(荒木飛呂彦) やっと1~3部が終わったので、少し休憩です

Slide 75

Slide 75 text

75 4. カードリーダーと連携する - SuicaのIDを読み取る - Run Python from Swift Read Suica ID using card reader

Slide 76

Slide 76 text

76 4. カードリーダーと連携する ’‘ ラズパイとPasori(RC-S380)を使って、SuicaのIDを読み取Y p‘ Ref: P‘ 事前にSwiftHomeAppで読み取って を確 認することに利用すY p‘ SuicaにはIDが割り振られていY !‘ 今回は余ってたSuicaを使用すY T‘ この認証結果を の判定に利用 https://www.sony.co.jp/Products/felica/consumerp 登録したIDと一致するか 在宅中か Pasoriで、SuicaのIDを読み取る

Slide 77

Slide 77 text

77 4. カードリーダーと連携する 帰宅時に に置いてある に 、認証! 玄関のラック Pasori Suicaをかざして Pasori連携で実現したいこと

Slide 78

Slide 78 text

78 4. カードリーダーと連携する ③SwiftHomePi SuicaのIDを通知 IDが一致するか 確認! ①SwiftHomeApp Postgress Vapor ②SwiftHomeServer Vapor Rest API Web Socket SuicaのIDをPost 認証用SuicaのIDを登録 Pasori連携の実装イメージ

Slide 79

Slide 79 text

79 4. カードリーダーと連携する EV 今回はPasoriでのデータを読み取るために、 という のモジュールを使用します1 HV Ref: DV を使って、SuicaのIDを読み込むPythonのコードを から呼び出します nfcpy Python nfcpy Swift https://github.com/nfcpy/nfcp’ nfcpy経由 Pasori連携の実装詳細

Slide 80

Slide 80 text

80 4. カードリーダーと連携する ラズパイに をインストールし、 との接続を確認する nfcpy Pasori # nfcpyのインストール # USBデバイスの一覧の中で、Pasoriが認識されているかを確認(IDはマスクしてます) # No such deviceと表示された場合 # Ref: https://www.out48.com/archives/5396/ $ sudo pip install -U nfcpy $ lsusb grep Sony Bus 001 Device 008: ID Sony Corp. RC-S380/S $ python -m nfc $ sudo reboot | > ******** Pasori連携の実装①

Slide 81

Slide 81 text

81 4. カードリーダーと連携する PythonでPasoriのデータを読みとる処理を実装する 1 2 3 4 5 6 7 8 9 10 import import : = = = - : : = return def () ( ) clf nfc. ( ) tag clf. (rdwr {'on connect' lambda tag False}) tag_id binascii. (tag._nfcid). () clf. () tag_id nfc binascii // Ref: https://qiita.com/h_tyokinuhata/items/2733d3c5bc126d5d4445 read_id print ContactlessFrontend connect hexlify decode close "Touch!" "usb" read_nfc.py Pasori連携の実装②

Slide 82

Slide 82 text

82 4. カードリーダーと連携する PythonKitとPathKitをPackage.swiftのdependenciesに追加 // (省略) // (省略) dependencies [ . ( : , : ), . ( : , : ), ], : package url branch package url from "https://github.com/pvieito/PythonKit" "master" "https://github.com/kylef/PathKit" "1.0.0" Package.swift Pasori連携の実装③

Slide 83

Slide 83 text

83 4. カードリーダーと連携する PythonKit Python Swift を使って、 の処理を から呼び出す 1 2 3 4 5 6 7 8 9 import import func -> let = let = let = return () { sys Python. ( ) path Path.current sys.path. (path) readNfc Python. ( ) readNfc. (). } PythonKit PathKit readNfcId // ①Pythonコードの呼び出しを記述しやすくするライブラリ // ②Pythonのファイルのパスを指定 // ③Pythonファイルのread_idを呼ぶ // ↑④Dynamic Member Lookup(コンパイル時にないプロパティへアクセス) Ref: https:// github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md String import append import read_id description "sys" "\( )/Sources/SwiftHomePi" "read_nfc" PythonCall.swift Pasori連携の実装④

Slide 84

Slide 84 text

84 4. カードリーダーと連携する 登録したIDと読み取ったIDが一致するかを確認する 1 2 3 4 5 6 7 8 9 10 let = guard let = try await else return if == else id (). () savedNfcId DataStore.shared. () { ( ) } id savedNfcId.nfcId { ( ) } { ( ) } PythonCall readNfcId fetchNfcId print print print // ①登録済みID取得 "Cannot find NfcId in sqlite DB" "This card is registered!" "This card is not registered" WebSocketManager.swift Pasori連携の実装⑤

Slide 85

Slide 85 text

85 4. カードリーダーと連携する 処理を動かして、動作確認してみる' 2 Suicaが2枚なかったので、余っていたnanacoと登録したSuicaで 動作確認しました! Pasori連携の実装⑥

Slide 86

Slide 86 text

86 4. カードリーダーと連携する

Slide 87

Slide 87 text

87 5. 人感センサーを使う - こちらから攻める - Run Bash from Swift Play warning sound Collaborate with Home appliance Communicate through GPIO

Slide 88

Slide 88 text

88 5. 人感センサーを使う ~u ラズパイと連携した が作動した時、↓を実行すw hu スピーカーで警告音を流b u SwitchBotでデバイスを動かb tu Push通知を送る(発表では省略9 7u カメラで侵入者を撮影し、Twitterに画像を投稿(今後実装予定9 Tu (①緯度経度や高度情報が家と一致しない、②重量セン サーに物が無い、③Suicaで認証されていない)場合のみ実行する 人感センサー 在宅中でない Ref: https://amzn.to/3QSQSei 人感センサーをトリガーに処理を実行する

Slide 89

Slide 89 text

89 5. 人感センサーを使う 玄関ラックに人感センサーを取り付け、侵入者の侵入を通知&警告する 侵入者検知 ①スピーカーから警告音再生 ③Push通知の送信 ②SwitchBotの デバイス操作 人感センサーを使うことで実現したいこと

Slide 90

Slide 90 text

90 5. 人感センサーを使う dx 回路の設定↓の通i hx コードの色は回路図と写真で同じでP Vx 電源2本、通信用1本(オレンジ/GPIO25)の3本を使用す1 #x に、LEDが点灯するようにしています 人を検知した時 人感センサーの連携の実装①

Slide 91

Slide 91 text

91 5. 人感センサーを使う GPIOインスタンスを用意する 1 2 3 4 5 6 let = ! let = ! = = = humanSensorGpio gpios[.P25] humanSensorLedGpio gpios[.P16] humanSensorGpio.direction .IN humanSensorLedGpio.direction .OUT humanSensorLedGpio. value 0 GpioManager.swift 人感センサーの連携の実装②

Slide 92

Slide 92 text

92 5. 人感センサーを使う 人感センサーの値の変化を監視する 1 2 3 4 5 6 7 8 9 10 humanSensorGpio.onChange { gpio gpio. { .humanSensorLedGpio. } { .humanSensorLedGpio. } } in if == = else = // ①値が変化した時に呼ばれる // Human detected // ②LEDを点灯する // (省略: 変更を通知) //③ LEDを消灯する // (省略: 変更を通知) value 1 self value 1 self value 0 GpioManager.swift 人感センサーの連携の実装③

Slide 93

Slide 93 text

93 5. 人感センサーを使う 人感センサーの値の変化の通知によって、処理を実行する 1 2 3 4 5 6 7 8 9 // ①監視用のメソッド // (省略: 人を検知した時の処理を実行) GpioManager.shared.observeHumanMoved { didDetected didDetected { ( ) } { ( ) } } in if else print print "Human detected!" "Human Not detected!" GpioManager.swift 人感センサーの連携の実装④

Slide 94

Slide 94 text

94 5. 人感センサーを使う 実際に処理を動かしてみる! 人感センサーの連携の実装⑤

Slide 95

Slide 95 text

95 5. 人感センサーを使う

Slide 96

Slide 96 text

96 5. 人感センサーを使う g… 侵入者に対して、侵入を検知していることを警告する音声を再Y W… サンプル用のmp3を のコマンドで再Y ’… Ref: f… Swiftから上記のサンプル音声を再生するShellスクリプトを実# F… 実行すると、ラズパイのイヤホンジャックに接続したスピーカーから 警告音声が流れる mpg123 https://www.mpg123.df mpg123 警告音を再生する実装の詳細

Slide 97

Slide 97 text

97 5. 人感センサーを使う SwiftからShellスクリプトを実行する共通処理を実装 1 2 3 4 5 6 7 8 9 10 // Ref: https://ja.stackoverflow.com/q/65654 @discardableResult func ... throws -> let = = = try return ( args: ) { task () task.executableURL ( : ) task.arguments args task. () task. () task.terminationStatus } shell _ String Int32 Process URL fileURLWithPath run waitUntilExit "/usr/bin/env" ShellCall.swift 警告音を再生する実装①

Slide 98

Slide 98 text

98 5. 人感センサーを使う Macの場合、Package.swiftに音声ファイルをリソースとして指定 resources [ . ( ), ] : // Copy resource file to bundle // Macの場合、ビルドの成果物はDerivedDataのディレクトリに生成される // Linuxの場合、cloneしたディレクトリにある音声ファイルを直接参照できる process "warning_voice.mp3" Package.swift 警告音を再生する実装②

Slide 99

Slide 99 text

99 5. 人感センサーを使う Mac/Linuxごとに、コマンドと音声ファイルのパスを指定する 1 2 3 4 5 6 7 8 9 10 let let if os = = else = = endif command: path: # ( ) command path Path.current # command path Path.current # String String Linux "/usr/bin/mpg123" "\( )/Sources/SwiftHomePi" "/opt/homebrew/bin/mpg123" "\( )/SwiftHomePi_SwiftHomePi.bundle/Contents/Resources" // If linux, returns repository path // If Mac, returns DerivedData path ShellCall.swift 警告音を再生する実装③

Slide 100

Slide 100 text

100 5. 人感センサーを使う Shellスクリプトを実行する関数に実行するコマンドを指定して、実行 1 2 3 4 5 6 7 let = do try catch voiceFilePath path { (command, voiceFilePath) } { (error) } "\( )/warning_voice.mp3" // ①コマンドとパラメーターは別々に指定する shell print ShellCall.swift 警告音を再生する実装④

Slide 101

Slide 101 text

101 5. 人感センサーを使う ag SwitchBotはAPIが公開されていて、一部のデバイスを除いて、 APIから状態の取得、操作ができc "g Ref: g 今回は↓の2つのデバイスを操作する https://github.com/OpenWonderLabs/SwitchBotAPp ←電源プラグ ネットワーク経由で電源のON/OFFを操作できる テープライト→ ネットワーク経由でライトのON/OFFを操作できる SwitchBotのデバイスを動かす実装詳細

Slide 102

Slide 102 text

102 5. 人感センサーを使う ƒs 電源プラグにIKEAで買った↓スタンド照明を繋t s 玄関ラックの横にスタンドを取り付けR 8s 人感センサーで検知した時に、スタンドの60W(すごい明るい)電球 をつけることで、侵入者の視界を奪う かなり眩しい SwitchBotの電源プラグを操作

Slide 103

Slide 103 text

103 5. 人感センサーを使う ‘太陽拳’システム 出典: DRAGON BALL(鳥山明)

Slide 104

Slide 104 text

104 5. 人感センサーを使う ‡ SwitchBotのAPIを実行するには、トークンが必E 9‡ SwitchBotアプリの 画面の を連2 a‡ をタップして、トークンをコピR €‡ 上記TokenをHeaderにセッˆ @‡ ↓のAPIを実行して、SwitchBotのデバイス一覧から操作したいデ バイスの を確認す‚ 9‡ (GET) 設定 アプリバージョン 開発者向けオプション deviceId https://api.switch-bot.com/v1.0/devices 太陽拳システムの実装準備

Slide 105

Slide 105 text

105 5. 人感センサーを使う 4B 今回は電源プラグの を実行する方法を紹介す% 5B ↓のEndpointのAPIでJSONをPostで実行する ON/OFF // EndPoint→ (POST)https://api.switch-bot.com/v1.0/devices/${DeviceId}/commands // Headers→ Authorization: ${SwitchBot-Token} // OFFにする場合、”turnOff” { : , : , : } "command" "parameter" "commandType" "turnOn" "default" "command" SwitchBotのAPIを実行してみる

Slide 106

Slide 106 text

106 5. 人感センサーを使う 人感センサーをトリガーに システムを動かす 人感センサー ↓ 警告音を再生/照明をつける/テープライトを点滅させる デモ

Slide 107

Slide 107 text

107 5. 人感センサーを使う

Slide 108

Slide 108 text

108 まとめ - の実装を終えて - SwiftHome

Slide 109

Slide 109 text

109 まとめ S1 普段書いている でもラズパイの開発ができH T1 を使うことで、Swiftで や を簡単に実装で きH V1 実装を追加することで、アイデア次第で一般販売されている 機能を拡張して、より便利に、幅広いことができるようになH 1 や など、 も必ƒ p1 様々な部品や道具などハードを追加で用意することも必要 Swift Vapor API Socket通信 スマート ホーム ラズパイ GPIO ハード面の技術キャッチアップ まとめ

Slide 110

Slide 110 text

110 まとめ ‚k 40分の発表は大変...笑(WWDCの人たちすごい!h k Swiftでラズパイの開発ができるのは楽し4 ik ハードを触るのはお金 がかかる...g Xk ラズパイでのSwiftの開発はMacのXcodeでの開発より制約が多4 lk Raspberry Pi OSだと、古いSwiftのバージョンしか使えな4 0k UbuntuOSだと、カメラの実装などハード周りの設定の実装が Raspberry Pi OSと比べて、ハードルが高い 感想

Slide 111

Slide 111 text

111 まとめ d˜ 人感センサーで検知した時に、カメラで写真を撮影し、TweeX ™˜ 二酸化炭素センサーの情報をSwiftUIのChartsでアプリに表5 7˜ 観葉植物に自動で or 定期的に水をあげi x˜ 土壌湿度センサーなども使ってみi e˜ 玄関の解錠・施錠をもっと便利にすi ɘ モーターを使って、アクションを起こす 今後やりたいこと

Slide 112

Slide 112 text

112 まとめ スマートホーム化してない人におすすめの初期セット!(約1万円) SwitchBotハブミニ スマホ スマートスピーカー エアコンやシーリングライト
 など、赤外線リコモンで操作 できる家電を操作! Œ{ 寝る前にベッドから、声でシーリングライトを消す(快適!!˜ ‹{ 家に着く前に、スマホからエアコンをつけて、帰る頃に涼しい or暖かい部屋にしておく!(夏と冬は至高!!) QOL爆上げ リモコンの赤外線の信号 を記憶&発進できる トリガー ちなみに。。。

Slide 113

Slide 113 text

113 まとめ d˜ 今回のスライドは を使用して、作成していまh T˜ 一般的なスライドツールより、UI作成の感覚に近W g˜ AutoLayoutやフラグの切り替え用の変数などが使え& gg˜ 色、フォントなどをアセットとして管理でき& ggg˜ 作成したレイアウトをコンポーネントとして、共通化でき& g‚˜ プラグインが充実してい& p˜ デザインへの理解を深めると、よりよいプロダクト開発へ繋がる Figma 今回の資料作成方法(おまけ)

Slide 114

Slide 114 text

114 まとめ hQ 『これ1冊でできる! ラズベリー・パイ 超入門 改訂第6版」(福田 和宏C @Q Vapor DocA )Q Swift on Raspberry P% eQ ラズベリーパイのレシ Q ラズパイにパソリを繋いでNFCタッチÆ IQ SwitchBot AP €Q https://amzn.to/3Azks3 €Q https://docs.vapor.codeA €Q https://www.slideshare.net/mostgood/swift-on-raspberry-pi-23009473’ €Q https://zenn.dev/kotaproj/books/raspberrypi-tipA €Q https://monomonotech.jp/kurage/raspberrypi/nfc.htmh €Q https://github.com/OpenWonderLabs/SwitchBotAPI 主な参考資料・情報

Slide 115

Slide 115 text

115 まとめ SwiftHome ブログ の発表内容やリポジトリの情報は、省略した内容も含め て、 に詳細を記載しています。 日本語 https://ulog.sugiy.com/iosdc-swift- home/ English https://ulog.sugiy.com/en/iosdc-swift- home/ ありがとうございました!