十二月 6, 2019

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でラップしたら使いやすさが一気に上がる気がする。