動機・目的
目的
EnOceanの環境発電型ドア開閉センサー(STM250J)の情報をラズパイで拾って、拾ったデータをESP3、ERP2に従い解釈をする。
動機
「環境発電」という単語を最近耳にすることが多くなってきたので、環境発電を利用して何かサービスめいたものが作成できないか検討する途中で、EnOceanの製品をご紹介いただいので実際に使ってみようと思い立った。
環境
ラズパイ:Raspberry Pi 3 Model B
OS:Raspbian 9
電文受信:USB400J(USB受信ドングル)
センサー:STM250J(ドア開閉センサー)
ラズパイにUSB400Jを刺してセンサーの情報を拾います。Raspbianをインストールしネットワーク設定が完了した後、各パッケージのアップデート完了後から作業を開始しています。ラズパイに入っているPythonのバージョンは2.7.13でした。
物はこちらで購入させていただきました。
実施内容
Pythonで受信した情報を解釈する
以下のソースでUSB400Jの受信している情報を確認し解釈します。
飛んできているデータはEnOcean Serial Protocol 3 (ESP3)というプロトコルに従ったものが飛んでくるので、仕様に従い解釈します。
# coding: UTF-8
import serial
import binascii
import threading
class Esp3:
data_length = 0
data = bytearray(0)
option_length = 0
option = bytearray(0)
packet_type = b'\x00'
crc8_header = b'\x00'
crc8_data = b'\x00'
def print_data(self):
print('data_length:%s' % self.data_length)
print('data:%s' % binascii.hexlify(self.data))
print('option length:%s' % self.option_length)
print('option:%s' % binascii.hexlify(self.option))
print('packet type:%s' % binascii.hexlify(self.packet_type))
print('crc8 header:%s' % binascii.hexlify(self.crc8_header))
print('crc8 data:%s' % binascii.hexlify(self.crc8_data))
class Erp2Thread(threading.Thread):
def __init__(self, esp3):
super(Erp2Thread, self).__init__()
self.esp3 = esp3
def run(self):
sub_telegram = self.esp3.option[0] #オプションからSub Telegramを取り出す。Send: 3 / receive: 1
dbm = self.esp3.option[1] * (-1) #オプションからdBmを取り出す。正負反転させる
erp2_header = self.esp3.data[0] #データからヘッダーとなる1バイトを取り出す
if erp2_header == ord(b'\x21'): #Originator-ID 32 bit nio Destination-ID、No extended header、1BS telegram のものだけ処理する
originator_id = binascii.hexlify(self.esp3.data[1:5]) #データからセンサーIDを取り出す
status_code = self.esp3.data[5] #データからセンサーのステータスを取り出す
button_status = ""
door_status = ""
if status_code == ord(b'\x09'): #センサーのステータスを人間が解る言葉に翻訳する。STM250Jはcloseとopenだけだが、ボタンのセンサーも同様のプロトコルらしい
button_status = "押されていません。"
door_status = "閉まっています。"
elif status_code == ord(b'\x08'):
button_status = "押されていません。"
door_status = "開いています。"
elif status_code == ord(b'\x01'):
button_status = "押されています。"
door_status = "閉まっています。"
elif status_code == ord(b'\x00'):
button_status = "押されています。"
door_status = "開いています。"
print( 'センサーID:%sは%s' % (originator_id , door_status) )
def main():
ser = serial.Serial('/dev/ttyUSB0',57600,timeout = 1)
sync_start = False
while True:
denbun = ser.read()
esp3 = Esp3()
if sync_start or denbun == b'\x55': #0x55で同期開始
sync_start = True
denbun = ser.read() #データ長を読み込み
data_length = 256 * ord(denbun)
denbun = ser.read()
data_length = data_length + ord(denbun)
esp3.data_length = data_length
denbun = ser.read() #オプション長を読み込み
option_length = ord(denbun)
esp3.option_length = option_length
denbun = ser.read() #パケットタイプを読み込み
packet_type = denbun
esp3.packet_type = packet_type
denbun = ser.read() #CRC8(Header)を読み込み
crc8_header = denbun
esp3.crc8_header = crc8_header
data = bytearray(data_length)
for loop_counter in range(data_length):
denbun = ser.read() #データを読み込む、長さ分読み込む
data[loop_counter ] = denbun
esp3.data = data
option = bytearray(option_length)
for loop_counter in range(option_length):
denbun = ser.read() #オプションんを読み込む、長さ分読み込む
option[loop_counter] = denbun
esp3.option = option
denbun = ser.read() #CRC8(Data)を読み込み
crc8_data = denbun
esp3.crc8_data = crc8_data
sync_start = False
if esp3.packet_type == b'\x0A': #パケットタイプがERP2のものだけスレッドで処理する
erp2_thread = Erp2Thread(esp3)
erp2_thread.start()
main()
実行結果は以下の通りです。センサーとマグネットを近づけたり遠ざけたりすると表示されます。同時接続も可能です。
センサーID:0400e559は閉まっています。
センサーID:0400e559は開いています。
センサーID:0400e559は閉まっています。
センサーID:0400e559は開いています。
センサーID:0400e6aeは閉まっています。
センサーID:0400e563は閉まっています。
センサーID:0400e559は閉まっています。
センサーID:0400e6aeは開いています。
センサーID:0400e563は開いています。
センサーID:0400e559は開いています。
センサーID:0400e559は閉まっています。
センサーID:0400e6aeは閉まっています。
センサーID:0400e6aeは開いています。
センサーID:0400e559は開いています。
センサーID:0400e6aeは閉まっています。
センサーID:0400e563は閉まっています。
センサーID:0400e559は閉まっています。
センサーID:0400e559は開いています。
センサーID:0400e563は開いています。
センサーID:0400e6aeは開いています。
センサーID:0400e6aeは閉まっています。
センサーID:0400e563は閉まっています。
センサーID:0400e6aeは開いています。
センサーID:0400e563は開いています。
センサーID:0400e563は閉まっています。
センサーID:0400e6aeは閉まっています。
センサーID:0400e559は閉まっています。
センサーID:0400e6aeは開いています。
センサーID:0400e563は開いています。
センサーID:0400e559は開いています。
解説
EnOcean系の製品は複数の仕様にまたがって作られています。
階層的なイメージでとらえていただくと解りやすいかもしれません。一番上のプロトコルがESP3になっています。
今回使用したSTM250JというセンサーはERP2(EnOcean Radio Protocol 2)という下位プロトコルを使用しているので、パケットタイプを識別してERP2のデータだけを処理しています。
センサーが送ってくるデータの実装はEEP(EnOcean Equipment Profiles)という仕様に基づき実装されているので、ERP2で受信したデータをEEPに基づき解釈しています。
詳細は仕様は公式のドキュメントをご参照ください。
参照ドキュメント
感想・今後
想像以上に長いソースになってしまいました。あと仕様を理解するのに時間がかかりました。
センサーのステータスは取得できるようになったので、あとはサービスを提供するサーバへデータを飛ばしていきます。