RaspberryPiとTWE-Liteで作る戸締り確認装置の製作記録

防犯のため、窓や玄関ドアが開かれた時に警報を出す防犯システムは各種あり、セキュリティ会社に取り付けてもらったり、DIYで簡単に取り付けすることもできる。その中でも、Panasonic製のワイヤレスセキュリティシステム「マモリエ(Panasonicの商標)」は、窓のクレセントの開閉を検知する機能を持ち、戸締りの確認を容易にできる防犯システムだ。しかし、この「マモリエ」は2020年3月で生産中止となってしまったので、それに代わる装置をRaspberry Piと超小型無線データ通信モジュールTWE-Liteを使って試作した。
●戸締り確認装置の部品構成と配線図
<親機の部品構成>

<使った部品>
・Raspberry Pi 4
・TWE-Lite ワイヤレスモジュール(TWE-Lite-DIP-UFL)
・TWE-Liteダイポールアンテナ(TWE-AN-P2010-050)
・白色OLED(SSD1306):電池電圧低下の警告表示のため
・I2Cの8ビットI/OエクスパンダIC(10LEDアレイを点灯させる)
・10LEDアレイ(OSX10201-R)
<子機の部品構成>

<使った部品>
・TWE-Lite ワイヤレスモジュール(TWE-Lite-DIP-UFL)
・TWE-Lite薄型アンテナ(TWE-A-P2010-05)
・ホールIC(SK8552 : UNISONIC TECHNOLOGIES製 )
<親機の結線図>

<子機の結線図>

●戸締り確認装置の仕組み
<クレセントの開閉状態を検知するTWE-Lite子機>
クレセント錠の可動部先端に取り付けたネオジウム磁石がホールICに近づいている状態が「閉」状態で、ホールICの出力端子が「High」となる。クレセント錠を開けた時に、磁石がホールICから遠ざかると、ホールICの出力端子が「Low」になり、モニターLEDが点灯、TWE-Lite子機のディジタル入力が「High」から「Low」に変化。
TWE-Lite子機はディジタル入力の状態が変化した時と、変化がなくても1分間隔で、状態をTWE-Lite親機に送信する。

<クレセントの開閉状態を収集するTWE-Lite親機>
TWE-Lite子機から送信されるディジタル入力状態をLEDアレイに点灯/消灯させる。子機の電池電圧が低下した時には、白色OLEDに表示する。
●使用したTWE-Lite
TWE-Lite DIPを使用。2018年に購入したTWE-Lite DIPには埋め込み型のアンテナタイプがあったが、現在はアンテナ別取り付け型しか売られていない。今回、子機2個には旧型のアンテナ埋め込み型を使った。
<TWE-LiteDIPの新旧比較>

<使用したTWE-Liteアプリと設定>
TWE-Lite-DIPに標準でインストールされている標準アプリ(App_Twelite)をそのまま使用した。標準アプリではディジタル入力が4入力しかないが、今回の場合1入力で足りることと、子機の電源電圧の状態をデータで送信できる。
(1)動作モード設定(ハードウェア設定)
・「子機:間欠10秒」モード設定:M3,M2,M1端子をGNDに接続

(2)インターラクティブモード設定
・論理デバイスID:親機(121)、子機3台(1,2,3)
・子機間欠10秒モードの間隔:60秒
<TWE-Lite子機から送信されるデータ事例>
受信データはstr型
“:01811501C0810DF83F0080590009E41A0101FFFFFFFFFF06”
1文字目はヘッダー”:”で、それ以降のデータの意味は

文字列データの意味

●プログラムの構成

●プログラム例
TWE-Liteデータを受信して処理するメインプログラム:TWE-Lite-Main.py
このプログラムの中で、次の2つのモジュールを使っているが、その説明は、プログラム事例を参照のこと。
(1)I2C_EXPANDA.py:「8ビットI/Oエクスパンダ(MCP23008/MCP23S08)を用いたLEDアレイの点灯プログラム例」を参照のこと
(2)oledSSD1306.py:「I2C制御の有機ELディスプレイモジュール(制御ICSSD1306)文字表示プログラム例」を参照のこと
import serial import threading from collections import deque import struct, binascii import I2C_EXPANDA import oledSSD1306 class TWE_Lite: def __init__(self): self.thStat = False self.data = deque() # 読み込みデータをQueにスタック self.serialPort = serial.Serial('/dev/ttyS0', 115200, timeout=0) # timeout=0:Non-blocking threadRead = threading.Thread(target=self.serialRead, daemon = True) threadRead.start() def serialRead(self): while True: self.inData = self.serialPort.readline() self.inData = self.inData.strip() # 前後の空白削除 if len(self.inData) > 0: self.inData = self.inData.decode('utf-8') # Byte型をstr型に変換 self.data.append(self.inData) def checkSum(self, checkData): checkData = checkData[1:] # ":"を削除 s24B = struct.Struct("24B") # 24bytes checkData = binascii.a2b_hex(checkData) # 16進数表記の文字列をバイナリデータに変換 checkData = list(s24B.unpack(checkData)) # 24個のIntデータに展開、変更可能なlistデータに変換 checkSumVal = checkData[23] # チェックサム=24番目最後 1byte evalCheckData = checkData[:23] # チェックサムを除く23個 # チェックサム計算 cSum = 0 for intVal in evalCheckData: cSum = cSum + intVal cSum = 0x100 - (cSum % 0x100) if cSum == checkSumVal: return True else: return False def dataRead(self): intData = [] if len(self.data) == 0: # Queが空の場合 return intData # self.data Queにデータがある場合 line = self.data.popleft() # データの取り出し if self.checkSum(line): # CheckSumがOKの場合 if line[0] == ":": line = line[1:] # ":"を削除 sData = struct.Struct(">BBBBBIBHBHBBBBBBBBB") line = binascii.a2b_hex(line) # 16進数表記のASCII文字列をバイナリデータに変換 intData = list(sData.unpack(line)) # 14個のIntデータに展開, 変更可能なlistデータに変換 if (intData[7] & 0x8000) == 0x8000: # 同じタイムスタンプ intData[7] &= 0x0FFF intData[9] = intData[9] / 1000 # 電圧(V) return intData #///////////////////////////////////////////////////////// if __name__ == '__main__': tweLite = TWE_Lite() arrayLED = I2C_EXPANDA.MCP23008() oled = oledSSD1306.OLED() lineStr = ["", "", "", ""] arrayLED.ALLOFF() BATT_LOW = 2.8 battLow = [0]*8 stat = True try: while stat: readData = tweLite.dataRead() if len(readData) > 0: print("送信元ID={:02d}, 通信品質LQI={:3d} Time={:8d}, 電圧={:8.3f}, 入力値={:04X}, Mask={:04X}".format( readData[0], readData[4], readData[7], readData[9], readData[11], readData[12])) if readData[9] < BATT_LOW: # 電池電圧低下 battLow[readData[0] - 1] = 1 # 子機IDの電池電圧低下を"1"に else: battLow[readData[0] - 1] = 0 # 子機IDの電池電圧低下を"0"にリセット lineStr[0] = ""; lineStr[1] = "" for idNo, battStat in enumerate(battLow): if battStat: lineStr[1] += (f'{idNo:02d} ') if len(lineStr[1]) > 0: # 電池低下の番号があれば lineStr[0] = "電池交換" oled.dispText(lineStr) if readData[11] == 1: # マグネットセンサーがONの時 arrayLED.ON([readData[0] - 1]) # ID=1 to 3 LED=0 to 2 else: arrayLED.OFF([readData[0] - 1]) except KeyboardInterrupt: stat = False oled.clearDisp() arrayLED.ALLOFF() arrayLED.close()
●試験状況
クレセントに取り付けた子機の他に、デモンストレーション用にブレッドボードに配線した子機2個を使って、開閉時の信号変化を試験した。
<試験の姿>
