Event

3. ラズパイでセンサーの値を読み取る – 重量センサーの値を取得する

3. ラズパイでセンサーの値を読み取る – 重量センサーの値を取得する

この記事はiOSDC2022で発表したセッション、Swiftで我が家を より便利に、安全に!の発表の第3部の内容をまとめたものになります。
この記事は、「2. Vaporを動かす- iPhoneとラズパイ間をVaporで通信3」 の記事の続編です。もしまだ読んでいない方は読んでいただけると!
この記事では、ラズパイと重量センサーを連携させて、その値を取得実装を紹介します。

2. Vaporを動かす- iPhoneとラズパイ間をVaporで通信 3この記事は、「Swiftで我が家をより便利に、安全に」の発表の2部「2. Vaporを動かす- iPhoneとラズパイ間をVaporで通信 」の内容の一部をまとめた記事になります!...

重量センサーの値を取得する

重量センサーで実現したいこと

今回はラズパイと重量センサーを連携させ、センサーの上に物が載っているかどうかを判定します
重量センサーは↓を使います!

今回はここにキーケースを置いて、

  • キーケースが置いてある -> 在宅中
  • キーケースがない -> 外出中

という判定ができるようにします

元々我が家で使ってるSwitchBot スマートロック↓を使って、自動で鍵が閉じた時に、リビングなどのライトを消したり、扇風機を消したり家電を操作してます
しかし宅配便の受け取りやゴミ捨てでドアを開ける→自動施錠の時にも、発動してしまい、毎回付け直すことになってます笑
なので、キーケースを持ち出した外出時のみ、ライトを消したりが作動するようにするのが、今回目指すゴールです!

現在の構成(Before)
実装イメージ(After)
用語解説
SwitchBotとは?
  • スマートホーム用のデバイスを販売する会社、プラットフォーム
  • 電球、プラグ、カーテン、加湿器、掃除機、開閉センサー、玄関ロック、テープライトなどをネットワークから確認・操作できるデバイスが販売できます(ここ←に書いてあるのは、全部我が家にあります笑)
Ref: https://www.switchbot.jp
IFTTTとは?

トリガーとイベントをレシピとして登録して、様々なサービス同士を連携できるサービスです。かなり幅広いサービスが使えるので、かなり使える便利サービスです(現在有料になっているのですが、便利なので、自分は課金勢です!笑)

  • GoogleHomeで話す→Merossのライトを消す
  • 指定時刻になる→SwitchBotでカーテンをあける
  • 家に近づく→NatureRemoでエアコンをつける

我が家のスマートホームの詳細
スマートホームの詳細や我が家のスマートホームの例は、以前とあるLT会でLTしたことがあったので、気になる方はその時の資料をチェック!

楽しい楽しいスマートホームへの誘い

重量センサーのデータを読み取る

今回の重量センサーは、24ビットの信号を出力するHX711というADコンバーターを使用しています。
詳細が記載されているドキュメントはこちら
データはSPIという形式で取得する必要があります。
通常は、ラズパイのGPIOピンからは0 or 1の値しか取得できないが、GPIO5(データ取得用), GPIO6(クロック信号用)を使用することで、重量のアナログ情報を24bitの単位で取得できます。

それでは実際のコードを確認してみましょう
まずはGPIOのインスタンスを用意しましょう
上記で記載したように、GPIO5とGPIO6のインスタンスを用意しています。

let gpios = SwiftyGPIO.GPIOs(for: .RaspberryPi4)
// Load sensor
let dataGpio = gpios[.P5]!
// serial clock
let sckGpio = gpios[.P6]!
dataGpio.direction = .IN
sckGpio.direction = .OUT

次にfor文を使って、24bitのデータを1bitずつdataIn変数に格納していきます。
for文の中で、bitシフトを使って1ずつdataInという変数に値を追加しています。

sckGpio.value = 0
var dataIn: UInt32 = 0
for _ in 0..<24 { // ①Get 24 -bit data 1bit at a time
    sckGpio.value = 1
    dataIn = (dataIn << 1) | UInt32(dataGpio.value) // ②Left bit shift → Reflect new bit in OR
    sckGpio.value = 0
}

次に取得した値が、ドキュメントに書かれた最大値と最小値の範囲に収まっているかをチェックします
そして③では補数にする処理を実行していますが、ここは正確に理解できていないです。。
最後に計算した値から初期値を引いて、校正(キャリブレーション)するために、計算した値、ここではrefernceUnitという定数で処理した値を返却します。

今回はこの処理をreadWeight()というメソッドに定義しました。

// 0x7fffff(16777215) is Max value, 0x800000(8388608) is Min value
let maxValue: UInt32 = 16777215 // ①Maximum data on the reference of HX711
let minValue: UInt32 = 8388608    // ②Minimum data on the reference of HX711

if (dataIn == maxValue || dataIn == minValue) { return 0 }
var signedData: Int32 = 0
if (Int32(dataIn & minValue) == 1) {
   // ③Execute the processing of the complement? (Understanding around here is not accurate. ..)
    signedData = Int32(((dataIn ^ maxValue) + 1))
} else {
    signedData = Int32(dataIn)
}
// ④Subtract the original initial value and returns the value processed by the calibration value (RefernceUnit).
return (Double(signedData) - defaultLoadValue) / refernceUnit
用語解説
校正(キャリブレーション)とは?
  • 正確な値を測るため、センサー個体ごとに誤差を調整すること
  • ↓のように重りを使い、複数の計測データの平均値を元に計算する(本当は分銅などがいいが、無いので硬貨で代用しました)
  • 今回はrefernceUnitを校正値として利用計算したら、refernceUnit = 465.375でした

次にセンサーの上に載っていたものを取ったことを検知するメソッドを作ります。
whileでループ文を作り、1秒ごとにセンサーの重量を取得します
そして30g以下の場合、completionを実行し、次にセンサーの上に物が載ったことを監視するメソッドを実行します。(コードはこちら)
ちなみに今回は物を置いている時にはLEDが点灯するようにもしてみました!

func observeLoadRemoved(completion: (() -> Void)) {
    while(true) {
        sleep(1) // ①Check every second
        if readWeight() > 30 { return } // ②If it is heavier than 30g, leave it as it is
        // ③(Omit: DB status change processing / LED lighting processing)
        completion()
        observeLoadPlaced {
            completion()
        } // ④If the object is removed, monitor the next thing that the object will be placed.
        break
    }
}

これでコードの実装は完了です!

次に回路を作りましょう!
図と写真を掲載します。
写真は拡張ボード(写真の虹色のケーブルがあるツール)を使っていること以外は、基本図と写真は基本同じ回路です!
今回は電源2本、通信用2本の合計4本を使用しています。

準備ができたら、ビルドしましょう!
動作確認した時のキャプチャ↓の通り、キーケースを置いた時と取り除いた時に、メッセージがprintされていて、物を置いている時にLEDが点灯させることができました!🎉

ラズパイでのSQLiteのセットアップ

ラズパイのSwiftHomeAppにデータを保存する際は、以前の記事で紹介したVaporのfluentを利用しています。

DBはSwiftHomeServerでPostgresを使っているのと、実装がシンプルそうだったので、SQLiteを使ってみました!
ただ今回は、Vapor自体の機能は一部の機能しか利用しないので、VaporのCLIでテンプレートプロジェクトを作成することはせず、直接Package.swiftに必要なライブラリを追加するようにしています。(ソースコード)

dependencies: [
     .package(url: "https://github.com/vapor/fluent.git", from: "4.0.0"),
     .package(url: "https://github.com/vapor/fluent-sqlite-driver.git", from: "4.0.0"),
// (Omit)
],

またSQLiteをラズパイで使用する時は、CLIで事前にインストールしておく必要があります↓。

$ sudo apt-get install libsqlite3-dev

基本fluentを使う場合は、Vaporのプロジェクト構成で実装することが多いので、fluent単独で使用することはあまりないので、情報が少なかったのですが、今回暗中模索で実装してみたので、もし気になる方はコードを参考にしていただけると!(もし実装方法で間違いがあれば、指摘いただけると!)

まとめ

今回は重量センサーを使って、その値を取得する実装をSwiftで書くことについて、まとめてみました。
重量センサーなどハードを使ってみて、すごい電子工作っぽくなってきて、すごい楽しかったです!

次は、「4. カードリーダーと連携する – SuicaのIDを読み取る」のまとめ記事になります!

4. カードリーダーと連携する - SuicaのIDを読み取るこの記事は、「Swiftで我が家をより便利に、安全に」の発表の4部「4. カードリーダーと連携する - SuicaのIDを読み取る 」の内容をまとめた記事になります!...

参考

+1

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA