スマートメーターから取得した電力消費量を可視化する (2)消費電力量を取得する

# この記事について
ロームBP35C0-T01とESP-WROOM-32を使って、スマートメーターから取得した電力量を可視化します。
[スマートメーターから取得した電力消費量を可視化する (1)機器の準備](https://qiita.com/nara256/items/49eff8c08abb78b69163)の続き、今回は電力消費量を取得します。

# スマートメーターから情報を取得するための実装
前回の記事で、ESP-WROOM-32(以下ESP32) と BP35C0-T1 を接続しましたので、今回は実際にスマートメーターからのデータを取得します。
と言いつつ、スマートメーターからの情報を取得することなどは二番煎じ三番煎じどころか**256番煎じ**くらいな感じなので、先人の知恵を借ります。

# 参考情報
以下ページの内容を参考にさせてもらいました。感謝。
- [Wi-SUN対応無線モジュール（BP35C0-T01）とラズパイを繋ぐ。（制作・実験編）](https://qiita.com/katsumin/items/acd567e9b8e81c691ed4)
- [Pythonでスマートメーターの情報を引っこ抜く](https://qiita.com/kanon700/items/d4df13d45c2a9d16b8b0)
- [スマートメーターの情報を最安ハードウェアで引っこ抜く](https://qiita.com/rukihena/items/82266ed3a43e4b652adb)

その他、目を通しておいたほうが良いサイトや資料は、文末に書いておきます。

## BP35C0-T01 とのやり取りについて
ざっくり以下の順番でBP35C0とコマンドをやり取りします。
以下、 `>>`の行がESP32からのデータ送信、`<<` の行がBP35C0からの応答です。

なお、詳細はBP35C0のコマンドリファレンスを確認してください。

### 事前準備
#### BP35C0 ファームウェアのバージョン確認　兼　コマンドのやり取りができることを確認する
```
>> SKVER
<< EVER 1.5.2
<< OK
```
`SKVER`を実行して、バージョン表示と`OK` が返ってくればOK

#### コマンドのエコーバックを無効に
やらなくてもいいと思うけど、エコーバック無効にしたほうがプログラム的に都合が良さそうなので、とりあえず。

```
>> SKSREG SFE 0
<< OK
```
`SKSREG `コマンドで`SFE `レジスタに`0`をセットする。`OK`が返ってくればOK

#### 応答を16進ASCII文字で返すように
```
>> WOPT 01
<< OK
```
`WOPT`コマンドで引数`01`(ASCII文字列の意味)を渡す。`OK`が返ってくればOK
#### BルートサービスのIDを設定する
```
>> SKSETRBID XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<< OK
```
`SKSETRBID`コマンドで、電力会社から入手したBルートのID(32桁の16進)をセットします。`XXXX`の部分は自分のIDに置き換えてください。`OK`が返ってくればOK
#### Bルートサービスのパスワードを設定する
````
>> SKSETPWD c XXXXXXXXXXXX
<< OK
````
`SKSETPWD`コマンドで、電力会社から入手したBルートのパスワードをセットします。`SKSETPWD`の後ろの`c`はパスワードの文字数の16進数です。16進で`c`なのでこの例ではパスワードは12文字、という意味です。
`OK`が返ってくればOKです。
###スマートメーターの存在をスキャンする
```
>> SKSCAN 2 FFFFFFFF 6 0
<< OK
<< EVENT 20 XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX 0
<< EPANDESC
<<   Channel:00
<<   Channel Page:11
<<   Pan ID:2222
<<   Addr:3333333333333333
<<   LQI:44
<<   Side:55
<<   PairID:66666666
<< EVENT 22 XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX 0
```
`SKSCAN`コマンドを実行すると、周辺に存在する機器のスキャンを行います。引数の`2`は「アクティブスキャンを行う」の意味、`FFFFFFFF`は対象機器をマスクしたい場合に指定（`FFFFFFFF`だと全機器を対象に）、`6`はスキャンの時間、最後の`0`は固定値です。
スキャンに対して周辺機器が応答すると、応答した機器の情報が出力されます。
コマンド応答に含まれる `Channnel` `Pan ID` `Addr` の値をメモしておきます。
なお、スマートメーターと通信できなかった場合は機器情報、つまり`EVENT 20`～`PairID:XXXXXXXX`までの行が返ってきません。
#### スマートメーター機器の登録
```
>> SKSREG S2 00
<< OK

>> SKSREG S3 2222
<< OK

>> SKLL64 3333333333333333
<< XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX
```
使用するスマートメーターの情報、つまり上記のスキャンで取得できた値をセットします。
`SKSREG S2` で引数に`Channel`の値を、`SKSREG S3`で引数に`Pan ID`の値を、`SKLL64`の引数に`Addr`の値を、それぞれ渡します。
`SKLL64`のコマンド応答の文字列(スマートメーターのIPv6値）をメモしておきます。

---
長かったですが、ここまでが事前準備です。

---

### データ取得開始
```
>> SKJOIN XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX
<< OK
```
`SKJOIN`コマンドに、上記`SKLL64`実行時の戻り値を引数に渡します。
これを実行すると、以後30分ごとにスマートメーターからの消費電力情報を受信するようになります。
以下のような値が、だいたい毎時0分と30分くらいに受信されます。

```
ERXUDP XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX YYYY:YYYY:YYYY:YYYY:YYYY:YYYY:YYYY:YYYY 0E1A 0E1A 0123456789ABCDEF 1 0 0026 1081000102880105FF017302EA0B07E30504161E0000005B93EB0B07E30504161E000000000C
```
ここで重要なのは、スペースで区切られたいくつかの値のうち、一番最後の部分の値、上記例でいうと`1081000102880105FF017302EA0B07E30504161E0000005B93EB0B07E30504161E000000000C`の部分です。

|No |開始桁 |文字数 |上記例での実際の値 |値の意味                              |
|:--|:------|:----|:------------------|:-------------------------------------|
|1 |8|6      |028801             |028801 だと、スマートメーターからの応答 |
|2 |22|2     |02                 |含まれる値の数 |
|3 |24|2     |EA                 |`EA`だと「正方向の定時積算電力量計測値」 `EB`だと「逆方向の定時積算電力量計測値」|
|4 |28|4     |07E3               |データ計測日の「年」の16進(0x07e3 = 2019)|
|5 |32|2     |05                   |データ計測日の「月」の16進(0x05 = 5)|
|6 |34|2     |04                   |データ計測日の「日」の16進(0x04 = 4)|
|7 |36|2     |16                   |データ計測日の「時」の16進(0x16 = 22)|
|8 |38|2     |1E                   |データ計測日の「分」の16進(0x1e = 30)|
|9 |40|2     |00                   |データ計測日の「秒」の16進(0x00 = 0)|
|10|42|8     |00005B93             |電力量(kWh*10)の16進(0x00005B93 = 2344.3kWh)|
|11|50以降 |   |EB0B07E30504161E000000000C|上記No.2「含まれる値の数」が2以上の場合は、No.3～10までを繰り返す|

ということで、このデータの場合、以下のような値の意味になります。

- 2019/05/04/ 22:30:00 の正方向の定時積算電力量計測値が2344.3kWh
- 2019/05/04/ 22:30:00 の逆方向の定時積算電力量計測値が1.2kWh

ちなみに、他にも瞬間電力計測値なども取得できますが、とりあえず今回は省略します。

## これらをESP32に実装してみる
前置きが長くなりましたが、実装してみます。
と、これらを1から実装するのは面倒でしょうから、ESP32向けにライブラリを作っておきました。
以下から取得してください。
https://github.com/nara256/Bp35c0_broute

###ライブラリのインポート方法
1. 上記URLのページから、緑色の `Clone or download` のボタンを押し、`Download ZIP`を選択し、zipファイルをダウンロードします。
2. Arduino IDEから、メニューの スケッチ→ライブラリをインクルード→ZIP形式のライブラリをインストール を選択し、1.でダウンロードしたファイルを選択します。

###実装してみる
Arduino IDEから、メニューの ファイル→スケッチ例→Bp35c0_broute→simple_get_value を開きます。
`#define`の行の`ID`/`PASS`の値は、自分のBルートのID/PASSに書き換えてから、ビルドしてESP32に書き込みます。

```cpp:simple_get_value.ino
#include "Bp35c0-broute.h"

#define ID    "YOUR_B_ROUTE_ID_HERE"
#define PASS  "YOUR_B_ROUTE_PASSWORD_HERE"

#define LED 2

HardwareSerial uart(2);
Bp35c0_broute b(uart);

void blink(int sec)
{
  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH);
  delay(sec);
  digitalWrite(LED, LOW);
  delay(sec);
}

void error()
{
  pinMode(LED, OUTPUT);
  while (true)
    blink(500);
}

void initLed()
{
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
}

void setup()
{
  Serial.begin(115200);
  uart.begin(115200);
  //b.setDebugSerial(Serial);

  initLed();

  if (!b.available())
    error();
  if (!b.open(ID, PASS))
    error();
}

void loop()
{
  blink(100);

  char u[32] = {'\0'}, d[32] = {'\0'};
  float uu, dd;

  b.waitForRecieve(d, &dd, u, &uu);
  Serial.println(u"正方向:" + String(d) + " : " + String(dd));
  Serial.println(u"逆方向:" + String(u) + " : " + String(uu));
}
```

だいたい、毎時0分ころ・30分頃に定時積算電力量計測値がスマートメーターから送られてきますので、それをSerialに出力します。

####実行結果
```
正方向:20190504223000 : 2344.30
逆方向:20190504223000 : 1.20

正方向:20190504230000 : 2344.50
逆方向:20190504230000 : 1.20

..(以下、30分毎に表示）
```


###サンプルスケッチの説明
ポイントを絞ってざっくり説明していきます。

##### ライブラリをインクルードします
```cpp
#include "Bp35c0-broute.h"
```

##### BP35C0-T01の接続先の設定
```cpp
HardwareSerial uart(2);
Bp35c0_broute b(uart);
```
BP35C0の接続先(UART2)を引数に、`Bp35c0_broute`のインスタンスをグローバル変数として生成します。

##### BP35C0-T01の初期設定
`setup()`関数内で、BP35C0-T01を初期化します。

```cpp
void setup()
{
  uart.begin(115200);
  if (!b.available())
    error();
  if (!b.open(ID, PASS))
    error();
}
```
UART2を115200bpsでオープンします。
`b.available()` == `TRUE` だと、BP35C0-T01と正常に通信できる状態です。
`b.open(ID, PASS)` を実行すると、引数で渡したBルートのID/PASSでBP35C0-T01を初期化します。`open()`関数の戻りが`FALSE`だった場合は、スマートメーターから応答がなかったなど、何らかの初期化失敗が発生したことを示します。

####定時積算電力量計測値を取得する
`loop()`関数内で、電力消費量を取得します。

```cpp
void loop()
{
  char u[32] = {'\0'}, d[32] = {'\0'};
  float uu, dd;
  b.waitForRecieve(d, &dd, u, &uu);
  Serial.println(u"正方向:" + String(d) + " : " + String(dd));
  Serial.println(u"逆方向:" + String(u) + " : " + String(uu));
}
```
`b.waitForRecieve()`関数を呼び出すと、定時積算電力量計測値をスマートメーターから受信するまで応答を待ちます。つまりこの関数を実行すると毎時0分・30分になるまでこの関数から返ってきません。
引数に渡した変数に、計測日時の文字列(YYYYMMDDHHMMSS)と電力量がセットされます。
ここで取得できる電力量は積算値です。なので、数値は右肩上がりで増えていきます。
なお、「正方向」とは消費した電力量、「逆方向」はたぶん自宅内で発電した電力だと思います。（私のうちはソーラー付きではないので「逆方向」の値は値が増えません。）

##まとめ & 次回予告
とりあえず、今回は画面(Serial)にスマートメーターから取得した電力量を表示してみました。
次回はIoT的に可視化してみます。

##参考資料
- [BP35C0](https://www.rohm.co.jp/products/wireless-communication/specified-low-power-radio-modules/bp35c0-product)
- [BP35C0-T01 仕様書](https://www.rohm.co.jp/documents/11546/4947397/bp35C0-T01_hardware_spec_v100_j.pdf)
- [「ROHM Sub-GHzシリーズ」サポートページ](https://micro.rohm.com/jp/download_support/wi-sun/index.php)
- [BP35C0 コマンドリファレンス](https://micro.rohm.com/jp/download_support/wi-sun/software/data/other/BP35C0_BP35C2-command_reference-dse-j.pdf) ※要認証 認証方法は[スターターガイド](https://micro.rohm.com/jp/download_support/wi-sun/data/other/bp35c0-startguide_v101.pdf)に書かれています。
- [ECHONET Lite規格書](https://echonet.jp/spec_v112_lite/)