SA-M0経由でECHONET Liteのスマートメーターにアクセスしてみた
概要
nodejsを使ってSA-M0を経由してスマートメーターの値を取得します。
SA-M0を経由することで、Wi-SUNアダプタの事を考えずに、LAN上にスマートメーターが存在しているように見えます。
まず最初に
ゼロスタートだったので、基礎知識を身につける為に以下を読みました。
ECHONET電文の作り方
これを読まないと echonet-lite
の出力が理解できません。略語だらけなので。。。
一応本稿でも必要な略語は解説していますがこちらの方が正しい解説です。
https://qiita.com/miyazawa_shi/items/725bc5eb6590be72970d
WiSUNを直接喋る場合とSA-M0を経由する場合の違い
http://route-b.iij.ad.jp/archives/128
Wi-SUNアダプタを使って直接やりとりする場合は、上記blogのスマートメーターの探索認証・接続の処理を行う必要がありますが、
SA-M0を経由した場合、この部分はすべてSA-M0がやってくれます。(ただし、スマホアプリで初期設定をすることが必要です)
SA-M0からデータを取得するアプリケーションは、単純に暗号化等の事を考えずに、リクエストを送信すれば応答してくれます。楽です。
Node.jsの echonet-lite ライブラリの使い方
今回必要なのは情報の取得だけですが、思いっきりドハマリしたので echonet-lite
ライブラリの使い方も含めて書いていきます。
機器スキャンのサンプルプログラム(下記URL) を題材にして説明します。
https://www.npmjs.com/package/echonet-lite
基本的な考え方
- ECHONET機器(SA-M0)と通信を行う為には、自分自身もECHONET機器になる必要があります。
- ECHONETの通信対象の区別は、
(IPを使う場合) IPアドレス + 機器ID
で行われます。 echonet-lite
的に命令の送信とその応答は非同期で扱う必要があります。
ECHONET機器になる部分は、echonet-lite
が受け持ってくれるので難しいことはありません。
今回であれば、自分自身はコントローラ (05ff01) になれば良いので、サンプルをそのまま使うことができます。
# 機器IDはなんでもよさそうですが、下手な機器を設定すると他のECHONET機器から発見されて状態取得系の命令が飛んでくるかもしれません。
やりとりは、命令を送信すると、それに対する応答が返ってくる。という単純なものです。(命令によっては、PUSH通知的な動きもありそうですが未確認)
落とし穴なのは、データ送信をした結果の応答は EL.initialize
の第三引数のfunctionに返ってくるという点です。
データの送信
EL.sendOPC1 = function( ip, seoj, deoj, esv, epc, edt)
これを使えばよい。 大事なことなので強調しますが、応答はEL.initializeの第三引数のfunctionで受け取ります
引数 | 内容 | 設定値 |
---|---|---|
ip | 要求先IPアドレス | 192.168.1.25 (EL.search()して見つけておく) |
seoj | 要求元機器ID | 05ff01 (コントローラー) *1 |
deoj | 要求先機器ID | 028801 (低圧スマート電力メーター) *1 |
esv | 命令コード | EL.GET (0x62: Get) *2 |
epc | 引数1(プロパティを指定する) | 0xE0 (積算電力量) *3 *4 |
edt | 引数2(セット系命令の値) | 空文字列(Get系命令であれば無視される為) *4 |
*1 機器ID
APPENDIX 機器オブジェクト詳細規定に規定されている。 (今回は、EL.search()して見つけた機器を片っ端から調べた)
https://echonet.jp/spec_object_rk/
2 命令コード
第二部 ECHONET Lite通信ミドルウェア仕様の 3-6ページに記載されている、 表 3-9 要求用ESVコード一覧に規定されている。
EL. 定数を見れば分かるかもしれない。
https://echonet.jp/spec_v113_lite/
*3 プロパティ (EPC)
APPENDIX 機器オブジェクト詳細規定に規定されている。 スマートメーターは 3.3.25 (3-290ページ)
https://echonet.jp/spec_object_rk/
*4 引数
規格上は、複数のプロパティを同時に要求できるが、 echonet-lite
ではその機能は実装されていない。
# 送ろうと思えば送れそうな関数はあるが、一個ずつ要求しても良いだろうという考えだと思われる。
データの受信
データの受信は、 EL.initialize
の第三引数の関数にて行われます。
第三引数の関数は function( rinfo, els, err )
となっており、それぞれ
引数 | 内容 | 設定値 |
---|---|---|
rinfo | 送信元情報 | IPアドレス以外は使わないかも |
els | 受信情報 | 主に扱う部分 |
err | エラー情報 | err != undefined ならエラーなので無視するなりなんなりする必要あり |
rinfoサンプルデータ
{address: '10.1.0.1xx', family: 'IPv4', port: 35110, size: 18 }
こんな感じなので、IPアドレス以外はつかわなそうです。
elsサンプルデータ
elsサンプルデータは、下記のコマンドを実行した際の応答例である。
EL.sendOPC1('10.1.0.100', '05ff01', '028801', EL.GET, "e8", "");
{ EHD: '1081',
TID: '0000',
SEOJ: '028801',
DEOJ: '05ff01',
EDATA: '7201e804001e0096',
ESV: '72',
OPC: '01',
DETAIL: 'e804001e0096',
DETAILs: { e8: '001e0096' } }
引数 | 内容 | 備考 |
---|---|---|
EHD | ECHONETバージョン | 1081固定 |
TID | トランザクションID | echonet-lite を使う限りは 0000固定 |
SEOJ | 送信元機器ID | 要求のDEOJと等しいはず |
DEOJ | 送信先機器ID | 自分自身の機器ID |
EDATA | 生データ | データ部を解釈しないでそのまま格納したもの |
ESV | 応答・通知用ESVコード | 72は GETに対する応答を表す *1 |
OPC | プロパティ数 | 回答データ数。echonet-lite を使う限りは 01 固定 |
DETAIL | データ詳細 | 解釈前データ |
DETAILs | データ詳細 | 解釈後データ。要求プロパティがキーになっている。この例だと e8を要求したのでe8だけが含まれる |
*1 ESVコード
第二部 ECHONET Lite通信ミドルウェア仕様の 3-6ページに記載されている、 表 3-10 応答・通知用ESVコード一覧に規定されている。
https://echonet.jp/spec_v113_lite/
注意点
-
自分が送信した命令も受信してしまうので、それは処理しないようにする必要がある。
例えば、 rinfo が自分自身のIPアドレスである場合は無視するとか、SEOJが自分の機器IDだったら無視するとか -
数値は16進数なので注意。
parseInt(value, 16)
で10進に変換可能。
プログラムの終了
echonet-lite
のサンプルプログラムを動作させると、いつまでまっても終了しない。これは、ソケットの受信待ちがずっと行われている為である。
プログラムを終了する為には以下の用にすれば良い。
(EL.initializeが内部で使用する dgram.socket
が返ってくるので、これをクローズすればイベントハンドラが終了できる)
var elsocket = EL.initialize( objList, function( rinfo, els, err ) {
(略)
if (データ受信完了) {
global.complete_flag = true; // グローバル領域にフラグを立てる
}
}
setTimeout(function(sock) {
if (global.complete_flag) { // データ受信完了なら
sock.close(); // sock = elsocket
}
, 10000, elsocket); // 関数に EL.initializeの返り値を渡す
雑なプログラムですが、こんな感じでフラグを監視するようにすればOKです。
まとめ
出来たものは、下記URLで公開しています。
https://github.com/yakumo-saki/b-route-reader
蛇足
sendOPC1をpromiseでラップしたら使いやすさが一気に上がる気がする。