.NET Core2.1でスマートメーターと戯れる Part1

# はじめに
家の電力メーターがスマートメーターに変わりました。
Bルートなるサービスで、瞬間電力量が取得できるらしいです。

Wi-SUNとECHONET Liteとかいう聞いたことなかったプロトコルを扱う必要がありますが、
C、Java、NodeあたりのSDKしか見つかりませんでした。

今回 .NET Coreで各プロトコルスタックをライブラリ化し、実装/公開しました。

# 前提知識
## Bルートとは
* スマートメーターが計量したデータを送るルートの種類
    * Aルートは電力会社（送配電事業者）
    * Bルートは家庭（HEMS）
* 電力会社へ申し込みが必要。以下が詳しい
    * [Bルートを利用するまでの流れについて](http://route-b.iij.ad.jp/archives/157)
        
## Wi-SUNとは
### 規格
* 特定小電力無線と呼ばれる920MHz帯の無線通信規格
* IEEE 802.15.4g/eとして規格化されている。以下が詳しい
    * [IOT規格　Wi-SUN HANとは？](http://iot-jp.com/iotsummary/iotstandard/iot%E8%A6%8F%E6%A0%BC%E3%80%80wi-sun-han%E3%81%A8%E3%81%AF%EF%BC%9F/.html)

    * [IoT規格　Wi-SUN Profile for ECHONET Liteとは？](http://iot-jp.com/iotsummary/iotstandard/iot%E8%A6%8F%E6%A0%BC%E3%80%80wi-sun-profile-for-echonet-lite%E3%81%A8%E3%81%AF%EF%BC%9F/.html)

### ドングル
* 以下のようなUSB接続タイプのドングルが販売されています
    * [ローム BP35C2](https://www.rohm.co.jp/products/wireless-communication/specified-low-power-radio-modules/information) ￥20,000 → Bルート/HAN デュアルスタック
    * [テセラ・テクノロジー RL7023 Stick-D/DSS](https://www.tessera.co.jp/rl7023stick-d_ips.html) ￥15,000 →Bルート/HAN デュアルスタック
    * [佐鳥電機 LPR9202 03-U1](https://www2.satori.co.jp/wireless/products_solutions/920mhz/920mhz02/6lowpan_howto.html) ￥14,000 →Bルートのみ
    * JORJIN WSR35A1-00 →Bルートのみ
    * [テセラ・テクノロジー RL7023 Stick-D/IPS](https://www.tessera.co.jp/rl7023stick-d_ips.html) ￥8,000 →Bルートのみ
    * [フレッツ・ミルエネ UDG-1-WSNE](https://web116.jp/shop/netki/miruene_usb/miruene_usb_00.html) ￥5,000 →Bルート/HAN デュアルスタック？(フレッツ・ミルエネの契約が必要)
## SK STACK IP
* http://www.skyley.com/products/skstack_ip.html
* Wi-SUN Profile for ECHONET Lite（TTC JJ-300.10 A方式）のリファレンス実装（らしい）
* 上記ドングルはたいていこのコマンド内臓している（ぽい）
* デュアルスタック系と、Bルートのみでコマンド体系が若干異なる（らしい）ので注意
* SKSTACKを実装したデバイスをJavaで扱うためのライブラリが公開されています
    * [GitHub SkyleyNetworks/SKSTACK_API](https://github.com/SkyleyNetworks/SKSTACK_API)/[Wiki](http://www.skyley.com/wiki/index.php?SKSTACK%20API%20for%20Java)
* ドングル買わないとコマンドリファレンスは見れないぽい。
    * ~~`BP35A1 コマンドリファレンスマニュアル`でぐぐるといいよ~~

## ECHONET Liteとは
* [Wikipedia](https://ja.wikipedia.org/wiki/ECHONET_Lite)によると、`エコーネットコンソーシアムが策定した通信プロトコル`で、`スマートハウス向け制御プロトコルおよびセンサーネットプロトコル`らしい。
* OSI参照モデル的には、5~7が仕様上定義されていて、4以下は仕様がない。
* 以下のオープンソース実装がある
    * [OpenECHO](https://github.com/SonyCSL/OpenECHO) Java実装 GPL3
    * [TOPPERS/ECNL](https://www.toppers.jp/ecnl.html) C実装？ BSD？
    * [HEMS（EchonetLite)認証支援センターのSDKとか](http://sh-center.org/sdk) Node実装 MIT?
    * [ELSwift](https://github.com/Hiroshi-Sugimura/ELSwift) Swift実装 MIT

### ECHONET Lite 通信ミドルウェア仕様
* [ECHONET Lite規格書](https://echonet.jp/spec_v113_lite/) 第2部 ECHONET Lite 通信ミドルウェア仕様にて規定
* フレームフォーマット(電文構成)、オブジェクト操作のシーケンスを規定している
* ざっくりいうと、オブジェクトインスタンスがあって、そのプロパティをGET/SETもしくはANNOUCEする

### ECHONET機器オブジェクト
* 具体的な機器のこと。
* クラスグループ
    * 機器クラスのグルーピング。１バイトのコードがグループに割り当てられており、以下のような分類となっている。
    * センサ関連機器、住宅設備関連機器(0x02)、健康関連機器、空調関連機器、管理操作関連機器、調理家事関連機器、ＡＶ関連機器
* クラス
    * 具体的な機器の種類、全体で200強のクラスがある。1バイトのコードがクラスに割り当てられている。
    * 例えば、低圧スマート電力量メータは0x88（住宅設備関連機器(0x02)）

* [APPENDIX ECHONET機器オブジェクト詳細規定](https://echonet.jp/spec_object_rk/)
* 550ページ以上あるPDFファイル
    * ~~公式データソースがPDFだけとか、普及させる気がないと思う。~~

# 本編

さて、前提知識はこのあたりにして、本題です

今回やりたいことは`スマートメーターから瞬間電力量を取りたい`

そのためには、Wi-SUNドングルを使って、SK STACK IPコマンドをたたき、
スマートメーターの認証を通して、ECHONETLiteフレームのやり取りを行う必要があります。

## プロトコル/つくったもの 全体像

![image.png](https://qiita-image-store.s3.amazonaws.com/0/260166/126100ab-5467-9f03-8d88-d1a08e06f5d2.png)

## SK STACK IP(1～5層)
SK STACK IPはコマンド/レスポンス+イベント形式のプロトコルです。
USBドングルはOSからは、シリアル通信用のポートとして認識されます。
ですので、すべての通信はシリアルポート経由となります。

### シリアル通信
.NET Core 2.1を使用するため、.NET Core 2.1では、Windowsしかシリアル通信ができません。
最終的にラズパイ上でも動作させたいため、今回はLinuxにも対応している[SerialPortStream](https://github.com/jcurl/SerialPortStream)を使用します。

* Linux上で動作させる場合
  * [こちら](https://github.com/jcurl/SerialPortStream#linux)に記載の通り、サポート・ライブラリー`libnserial.so`をコンパイルする必要があります
  * また、libnserial.so.1.1をlibnserial.so.1にリネームして、アセンブリ`SkstackIpDotNet.dll`と同じフォルダに配置する必要があります[このへん?](https://github.com/dotnet/core/issues/740)

### 出来上がったもの
SKSTACK-IPのAPIラッパーライブラリを作成しました。

RL7023 Stick-D/IPSに付属のコマンドリファレンスマニュアルをもとに全コマンドを実装しています。
GitHubにて公開しています。
→[SkstackIpDotNet](https://github.com/HiroyukiSakoh/EchoDotNetLite/tree/master/SkstackIpDotNet)

### 使い方ざっくり
```c#:抜粋
//Windows
string devicePort = "COM3";
//こんな分岐でいいのだろうか(不安) そもそも ポートってどう選択すべき…？
if (System.Environment.OSVersion.Platform.ToString() != "Win32NT")
{
    //Linux等(Raspbian Stretchで動作確認)
    devicePort = "/dev/ttyUSB0";
}
//SKDeviceクラスをインスタンス化します
using (var skdevice = serviceProvider.GetService<SKDevice>())
{
    //シリアル通信を開く
    skdevice.Open(devicePort, 115200, 8, Parity.None, StopBits.One);
    //EVENTのイベントハンドラを登録
    skdevice.OnEVENTReceived += (sender, e) =>
    {
        logger.LogDebug(e.ToString());
    };
    //ERXUDP(UDP受信)のイベントハンドラを登録
    skdevice.OnERXUDPReceived += (sender, e) =>
    {
        logger.LogDebug(e.ToString());
    };

    //各種コマンドはSKDeviceクラスにメソッドとして実装
    var eInfo = await skDevice.SKInfoAsync();
    logger.LogInformation(eInfo.ToString());

    var eVer = await skDevice.SKVerAsync();
    logger.LogInformation(eVer.ToString());
}
```
詳細は以下
→[SkstackIpDotNet.Example](https://github.com/HiroyukiSakoh/EchoDotNetLite/tree/master/SkstackIpDotNet/SkstackIpDotNet.Example)
→[EchoDotNetLiteSkstackIpBridge](https://github.com/HiroyukiSakoh/EchoDotNetLite/tree/master/EchoDotNetLiteSkstackIpBridge)

### ハマったポイント
UDP受信＝ERXUDPイベントのペイロードは16進数文字列なのに、
UDP送信＝SKSENDTOコマンドのペイロードはバイナリっていう。
コマンドリファレンス上は両方ともCHAR[]なのに…

### SKコマンド
Bルートで必要となるSKコマンドはそこまで多くありません。

|コマンド|説明|用途|
|:--------|:--|:--|
|SKINFO|ドングルの通信設定値を取得するコマンド<br>レスポンスはEINFO|リンクローカルアドレスを取得し、<br>自ノードの識別に使用する|
|SKSETPWD|パスワードを設定するコマンド|Bルートのパスワードを設定する|
|SKSETRBID|RouteBIDを設定するコマンド<br>PairingID（SAレジスタ）に値が設定される<br>（スキャン時の応答が該当メーターのみに絞られる？）|BルートのIDを設定する|
|SKSCAN|PANをスキャンするコマンド。|メーターをスキャンする|
|SKSREG|レジスタに値を設定するコマンド|スキャンの結果から、S02(自端末が使用する周波数の論理チャンネル番号)、<br>S03(自端末の PAN ID）を設定する|
|SKLL64|64 ビット MAC アドレスを IPv6 リンクローカルアドレスに変換するコマンド|スキャンの結果から、メーターのIPv6アドレスを取得する|
|SKJOIN|PANA 認証クライアントとして、PANA接続シーケンスを行う<br>接続結果はイベント(EVENT)で通知される。|メーターへの接続、認証を行う|
|SKSENDTO|UDPでデータを送信するコマンド|ECHONETLiteフレームの送信を行う|
|ERXUDP|コマンドではなく、イベント<br>UDP(マルチキャストも含む)を受信すると通知される|ECHONETLiteフレームの受信を行う|

### メーターとの接続完了までのコマンド/シーケンスの例
<details><summary>通信シーケンス</summary><div>

```powershell
#自ノードIPv6アドレスの取得
> SKINFO
SKINFO
EINFO [自ノードIPv6アドレス] 100723FFFEA07461 3B 1094 FFFE
OK
#パスワードの設定
> SKSETPWD C XXXXXXXXXXXX
SKSETPWD C XXXXXXXXXXXX
OK
#IDの設定
> SKSETRBID XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
SKSETRBID XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
OK
#スキャン開始
#スキャン時間:4
> SKSCAN 2 FFFFFFFF 4
SKSCAN 2 FFFFFFFF 4
OK
EVENT 20 [自ノードIPv6アドレス]
EPANDESC
  Channel:3B
  Channel Page:09
  Pan ID:1094
  Addr:[メーターMACアドレス]
  LQI:1D
  PairID:005B23E3
EVENT 22 [自ノードIPv6アドレス]
#PAN発見: 論理チャンネル番号:3B,チャンネルページ:09,PAN ID:1094,アドレス:[メーターMACアドレス],RSSI:1D,PairingID:005B23E3
#S02(自端末が使用する周波数の論理チャンネル番号)設定
> SKSREG S2 3B
SKSREG S2 3B
OK
#S03(自端末の PAN ID）設定
> SKSREG S3 1094
SKSREG S3 1094
OK
#メーターのIPv6アドレスを取得
> SKLL64 [メーターMACアドレス]
SKLL64 [メーターMACアドレス]
[メーターIPv6アドレス]
#PANA接続シーケンス開始
> SKJOIN [メーターIPv6アドレス]
SKJOIN [メーターIPv6アドレス]
OK
EVENT 21 [メーターIPv6アドレス] 02
EVENT 02 [メーターIPv6アドレス]
# 鍵交換など(SKJOINのコマンドで勝手に行われる)
ERXUDP [メーターIPv6アドレス] [自ノードIPv6アドレス] 02CC 02CC [メーターMACアドレス] 0 0028 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
EVENT 21 [メーターIPv6アドレス] 00
ERXUDP [メーターIPv6アドレス] [自ノードIPv6アドレス] 02CC 02CC [メーターMACアドレス] 0 0068 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
EVENT 21 [メーターIPv6アドレス] 00
ERXUDP [メーターIPv6アドレス] [自ノードIPv6アドレス] 02CC 02CC [メーターMACアドレス] 0 0054 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
EVENT 21 [メーターIPv6アドレス] 00
ERXUDP [メーターIPv6アドレス] [自ノードIPv6アドレス] 02CC 02CC [メーターMACアドレス] 0 0058 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
EVENT 21 [メーターIPv6アドレス] 00
#EVENT ID＝0x25:PANA による接続が完了
EVENT 25 [メーターIPv6アドレス]
```
</div></details>

# ECHONET Lite(6～7層)
長くなったので次回に続きます

# 動作確認環境
* OS:Windows10/Raspbian Stretch
* ミドルウェア:.NET Core 2.1 Runtime v2.1.6
* Wi-SUNモジュール： [RL7023 Stick-D/IPS](https://www.tessera.co.jp/rl7023stick-d_ips.html)
* 低圧スマート電力量メータ:中部電力管内(2機)

# 参考
* [スマートメーターの情報を最安ハードウェアで引っこ抜く](https://qiita.com/rukihena/items/82266ed3a43e4b652adb)
* [スマートメーターとECHONET Liteで通信できるようになるまで](http://route-b.iij.ad.jp/archives/128)
* [HEMS-スマートメーターBルート(低圧電力メーター)運用ガイドライン第4.0版](http://www.meti.go.jp/committee/kenkyukai/shoujo/smart_house/pdf/009_s03_00.pdf)
* [エコーネット規格　（一般公開）](https://echonet.jp/spec_g/)
* [MoekadenRoom (機器オブジェクトエミュレーター)](https://github.com/SonyCSL/MoekadenRoom/blob/master/README.jp.md)
* [GitHub SkyleyNetworks/SKSTACK_API](https://github.com/SkyleyNetworks/SKSTACK_API)
* [OpenECHO (ECHONET Liteのjava実装)](https://github.com/SonyCSL/OpenECHO)