chakokuのブログ(rev4)

日々のごった煮ブログです

最後の接続経路、、EchonetLiteを復習しながらパケットを投げてみる

<背景>
Amazon Echoに話しかけて、AWS IoT Core経由で機器を制御する試作を開発中、最終的には、「エアコンをつけて」と発話するとEchonetLite接続のエアコンが動くようにしたい

<要約>
先人のサンプル等を参考にしつつ、PythonUDPプログラムを書いてEchonetLite機器との最低限の通信を確認できた。

<詳細>
Amazon Echoに「エアコンをつけて」と発話するとAWS IoT Core経由でラズパイと通信し、EchonetLite接続のエアコンが動くようにしたいと考えています(今は発話でLED点灯までできたところ)。
かなり昔にEchonetLiteをUDPで制御する試作プログラムを作ったことがあったけど、マルチキャスト通信と普通の通信(ユニキャスト)をどうやって併存させたらいいのか、Pythonでの実装方法がわからず、機器制御プログラム作成は頓挫した。音声で発話して、EchonetLiteの機器を制御させたいので、再度トライしたい。

バイスを見つけるにはデバイスからマルチキャストでINFが配信されるのをコントローラが受ける必要があるのだが、、もしコントローラ側でのマルチキャスト通信の実装が難しかったら、デバイスは見つかったとしてユニキャストによる通信だけを実装する。EchonetLiteで通信するサンプルは結構記事があるけど、マルチキャストでINFを受ける所をどのように実装しているのか?が興味深い(INF以外にGET等もマルチキャストで実装しているのか!?)。

5年前の自分の記事を読み直すと、無線GW(メディアコンバータ)(Panasonic CF-TC7B)からは以下のプロパティが返却されるはずなのだが。。

送信元IP:192.168.11.XXX 送信元PORT:49XXX
 DATA:EHD1:10, EHD2:81, TID:0000, SEOJ:0EF001, DEOJ:05FF01, ESV:72, OPC:01, EPC1:D6, PDC1:07, EDT1:0205FF01013005, 

Windows版SSNGはWIndows10では動かないかも。。と書かれているので、Node.JS版SSNGを使った。SEARCH機能で端末を検索したところ、以下の応答が得られた。NodeJS版SSNGの画面は以下。UDPマルチキャストで探すので、IP:224.0.23.0,Port:3610となる。

コンソール側に出力されたログは以下

UDP receive:
        rinfo=  { address: '172.17.37.157', family: 'IPv4', port: 53740, size: 14 }
        msg=  <Buffer 10 81 00 03 05 ff 01 0e f0 01 62 01 d6 00>
UDP receive:
        rinfo=  { address: '172.17.37.157', family: 'IPv4', port: 35151, size: 18 }
        msg=  <Buffer 10 81 00 03 0e f0 01 05 ff 01 72 01 d6 04 01 05 ff 01>

addressに表示されている172.17.37.157は仮想環境のUbuntuのIPだった。

メディアコンバータ(無線GW)が見つかったと思ったけど、メディアコンバータに接続しているはずのエアコンの情報が返ってこないがおかしいのと、メディアコンバータを抜いてスキャンしてもやっぱりデバイス応答があるので、これは別のEchonetLiteが返却しているのだと判断。肝心のメディアコンバータは返事していない。

LANから不要な機器をすべて外して、メディアコンバータとキャプチャPCだけの状態で、メディアコンバータを再起動した。この結果、メディアコンバータは以下のパケットを投げていた。

src:192.168.10.151   dst:224.0.23.0 :3610
EHD1 HED2 TID   SEOJ   DEOJ   ESV OPC EPC PDC EDT
10   81   0001  0ef001 0ef001 73  01  d5  07  02 05ff01 013009
                                      (*)
*:D5 : インスタンスリスト通知

EchonetLite仕様メモ

EOJ:
05FF01: コントローラ
0EF001: ノードプロファイル
0130NN: エアコンオブジェクト
0290NN: 照明

ESV: (EchonetLite Service)
0x60 SetI
0x61 SetC
0x62 Get
0x63 INF_REQ
0x6E SetGet
....................
0x71 Set_Res  (res for 0x61)
0x72 Get_Res  (res for 0x62)
0x73 INF      (自発 or res for 0x63)
0x74 INFC(応答要)
0x7A INFC_Res (res for 0x74)

          SRC    DEST
1081 0007 05ff01 0ef001 62 01 d6 00
1081 0007 0ef001 05ff01 72 01 d6 0401 05ff01

EPC 
== エアコン ==
0x80 電源  0x30:ON  0x31:OFF
0xB0 動作モード  0x41:Auto,0x42:Cool,0x43:Heat,0x44:Dry,0x45:Wind
0xB3 設定温度   符号付1B

== 照明 ==
0x80 電源  0x30:ON  0x31:OFF

SSNGでパケット送受信させたかったけど、何が悪いのかどうも正しくパケットが出ていないようである。メディアコンバータのIPが判明し、メディアコンバータからはEchonetLiteパケットを出力していることも分かったので、PythonUDP送受信サンプルを書いてみる。
IPが分かっている場合に、先方の機器に何がぶら下がっているか?は、INF_REQを送ってインスタンス一覧をもらう
以下はUDBユニキャストの送信ソース

#!/usr/bin/python3 
import socket

ip = "192.168.10.151"    # MediaConverter
ECHONETport = 3610

# infreq(0x0x63) with ins list(0xD6) ask to NodeProfile(0x0EF001)
msg = "1081000205FF010EF0016301D600"   
msg = bytes.fromhex(msg)

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(msg, (ip, ECHONETport))

ノードプロファイルクラス(0x0EF001)にINF_REQを投げて、配下のオブジェクト一覧をもらおうとすると、ノードプロファイルクラス(メディアコンバータ)からはMulticastで返事を送ってくる。だから、、制御ソフト側は、MulticastとUnicastの両方を受け取れるように実装している必要がある。過去にPython版のEchonetLiteコントローラを作ろうとして挫折した原因は、MulticastとUnicastを同時に受信する実装方法が分からなかったため。まぁ課題を突破する以前のレベルで、頭の中も整理できていなくてどうアプローチしたらいいのかも検討できないまま終わった。UDPでのMulticast/Unicastを同時に受信するサンプルを書いてくださっている人(saito様)がいて参考に実装したら、確かに両方のパケットが受信できた。ソースは以下

#!/usr/bin/python3
# original source:
# https://memo.saitodev.com/home/python_network_programing/#id5

import socket

PORT = 3610
bufsize = 1024

LOCAL_ADDR = '192.168.10.150'
MULTICAST_GROUP='224.0.23.0'

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', PORT))
sock.setsockopt(socket.IPPROTO_IP,
                socket.IP_ADD_MEMBERSHIP,
                socket.inet_aton(MULTICAST_GROUP) + socket.inet_aton(LOCAL_ADDR))

while True:
  data = sock.recvfrom(bufsize)
  print(data)

sock.close()

応答パケットは以下(清書できていませんが、1行目はMulticastによる受信パケットで、2行目はUnicastによる受信パケット)

(b'\x10\x81\x00\x02\x0e\xf0\x01\x05\xff\x01s\x01\xd6\x07\x02\x05\xff\x01\x010\t', ('192.168.10.151', 49154))
(b'\x10\x81\x009\x0e\xf0\x01\x0e\xf0\x01b\x02\xd6\x00\x8a\x00', ('192.168.10.151', 49154))

これができるなら、、INFREQをMulticastで投げることで、EchonetLite機器がどのIPに割り当たってるかもわかるはず。。引き続きsito様の記事を参考にして、MulticastのUDBでINF_REQを送信するサンプルを実装

#!/usr/bin/python3 
# org source:
# https://memo.saitodev.com/home/python_network_programing/#id5

import socket

MULTICAST_GROUP='224.0.23.0'
LOCAL_ADDR = '192.168.10.150'

ECHONET_PORT = 3610

msg = "1081000205FF010EF0016301D600"   # infreq D6(ins list) ask to node prop
msg = bytes.fromhex(msg)

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(LOCAL_ADDR))

sock.sendto(msg, (MULTICAST_GROUP, ECHONET_PORT))

sock.close()

上記Multicast配布後、以下のパケットが受信できた。1行目は自分が発信したマルチキャストを自分も受けてしまったということかと。2行目~3行目はメディアコンバータからの返信。パケットは詳細に見ていませんが、オブジェクトリスト等が含まれていると思う。。多分。。

(b'\x10\x81\x00\x02\x05\xff\x01\x0e\xf0\x01c\x01\xd6\x00', ('192.168.10.150', 54742))
(b'\x10\x81\x00\x02\x0e\xf0\x01\x05\xff\x01s\x01\xd6\x07\x02\x05\xff\x01\x010\t', ('192.168.10.151', 49154))
(b'\x10\x81\x00=\x0e\xf0\x01\x0e\xf0\x01b\x02\xd6\x00\x8a\x00', ('192.168.10.151', 49154))
(b'\x10\x81\x00>\x0e\xf0\x01\x0e\xf0\x01s\x01\xd5\x07\x02\x05\xff\x01\x010\t', ('192.168.10.151', 49154))

■追記(20201115)
送信されるデータを確認すると、対象ノード配下には、コントローラ(0x05FF01)とエアコン(0x013009)がぶら下がっているとの回答。

          SEOJ   DEOJ   ESV OPC EPC PDC EDT
1081 0002 0ef001 05ff01 73  01  d6  07  02 05ff01       013009
                        INF     *   N      コントローラ エアコン

          SEOJ   DEOJ   ESV OPC EPC PDC EDT
1081 000e 0ef001 0ef001 62  02  d6  00  8a00
                        GET     *   0

(*)D6 : 自ノードインスタンスリスト

フィールドの意味(仕様書(ECHONET Lite通信ミドルウェア仕様)より引用)

SEOJ:送信元ECHONET Liteオブジェクト指定 (3B)
DEOJ:送信先ECHONET Liteオブジェクト指定 (3B)
ESV: ECHONET Liteサービス(1B)
OPC: 処理プロパティ数(1B)
EPC: ECHONET Liteプロパティ(1B)
PDC: EDTのバイト数(1B)
EDT: プロパティ値データ(PDCで指定)

エアコンのオブジェクトが分かった(0x013009)ので、これに向かってSET要求(0x62)を出せばエアコン操作ができるはず。。

■追記
エアコンクラスに以下を投げた(GET(0x62) / 動作状態 0x80)

# S:108100 01 05FF01 013009 62 01 80 00

以下が返却された(動作状態:0x80, 未動作中(0x31))

# R:108100 01 013009 05ff01 72 01 80 01 31

確かに、今はエアコンが切れている。

■追記(201115)
エアコンオブジェクトに以下の電文を投げてみる。自宅のエアコンがONになった*1

1081000105FF01 013009 60 01 80 01 30

上記電文を投げるプログラムは以下(エアコンのIP,機器オブジェクトは各自の環境依存)

#!/usr/bin/python3

import socket

TARGET_IP = "192.168.10.XXX"
ECHONET_PORT = 3610

EHD1 = '10'
HED2 = '81'
TID  = '0001'
SEOJ = '05FF01'
DEOJ = '013009'   # AIRCON
ESV  = '60'       # SET
OPC  = '01'
EPC1 = '80'       # Operating Status
PDC1 = '01'
EDT1 = '30'       # ON 
#EDT1 = '31'       # OFF

msg = EHD1 + HED2 + TID + SEOJ + DEOJ + ESV + OPC + EPC1 + PDC1 + EDT1
print(msg)
msg = bytes.fromhex(msg) 
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(msg, (TARGET_IP, ECHONET_PORT))
sock.close()

■過去の記事
自宅エアコンをEchonetLiteで叩いた時の記事(5年前。。)
自宅のエアコンをEchonetLiteで叩いてみる。。 - chakokuのブログ(rev4)
Echonet liteの調査 - chakokuのブログ(rev4)


■仕様書や先人の情報
EchonetLite通信仕様書
https://echonet.jp/wp/wp-content/uploads/pdf/General/Standard/ECHONET_lite_V1_12_jp/ECHONET-Lite_Ver.1.12_02.pdf

制御対象機器ごとの仕様書(各機器がどんなプロパティを持っているか?)
https://echonet.jp/wp/wp-content/uploads/pdf/General/Standard/Release/Release_J_jp/Appendix_Release_J.pdf


http://sh-center.org/120620/downloads/seminar_151002.pdf
https://github.com/SonyCSL/OpenECHO/blob/master/Processing/Tutorial.pdf?raw=true

UDPによるMulticastプログラミングサンプル・・参考になりました
Pythonでネットワークプログラミング | saito's memo
(5年前は挫折しましたが、このサンプルのおかげで解決できた)

*1:妻には今エアコンいらんと怒られる。。