スマートメーターの情報を最安ハードウェアで引っこ抜く

# はじめに

電力自由化にともない、**スマートメーター**の設置が進められています。

電力会社が設置した**スマートメーター**と、各社から販売されている**HEMS見える化端末的なモノ**を導入すると**消費電力などをリアルタイムに見る**ことができるようになります。

しかし！、**HEMS見える化端末的なモノ**は**住宅リフォーム業界的なプライス設定**ですんごく**お高い**んです。

そこで、**HEMS見える化端末的なモノ**を**安価に自作しちゃおう**というのがこの記事の趣旨です。

# おことわり

東京電力供給区域での話を前提としていますが、他社供給区域にお住まいの場合は電力会社名を読み替えてください m(_ _)m

なお、通信の物理レイヤは各社で決められるのですが、フタを開けてみたら全国Wi-SUN(後述)で統一されてました。購入すべきハードウェアは全国どこでも変わりません。

# ハードウェア
## スマートメーターを設置してもらう
今となっては特別な事情が無い限り、スマートメーターが設置されてます。

## Bルートサービスの利用申し込みをする

突然、知らない言葉が出てきました。

**Bルート**とは、スマートメーターと、自宅のHEMS機器との間の通信ルートのことを指します。（AルートやCルートも用語としてはあるのですが、脇道それちゃうので説明省略。）

Bルートサービスの利用申し込みをすると、スマートメーターのIDとパスワードを教えてもらえます。利用料はタダです。

電力自由化で電力会社を乗り換えても、Bルートサービスの申込先は東京電力になります。正確に言うと、送配電を担当している**東京電力パワーグリッド株式会社**です。

スマートメーターに交換される前でも、Bルートサービスの利用申し込みをすると暗黙的にスマートメーターへの交換を申し込んだことになります。

下記リンクから申し込みましょう
[電力メーター情報発信サービス（Bルートサービス）](http://www.tepco.co.jp/pg/consignment/liberalization/smartmeter-broute.html)
（東京電力以外では、電力会社名とBルートサービスでググってみてください）

そのページの注意書きに
>※ＨＥＭＳ機器などお客さま宅内の設備はお客さまにてご用意ください。

と書かれていますが、「ＨＥＭＳ機器などお客さま宅内の設備」をこの記事の下の方で作りますって話です。

ちなみに、IDは郵送で、パスワードはメールで送られてきます。逆じゃねーのかよ！

ちなみにちなみに、IDが送られてくる封筒は、送り主がハンコで押されているショボイものです。東電のやる気~~のなさ~~を感じさせてくれます。
![DSC_0472.JPG](https://qiita-image-store.s3.amazonaws.com/0/104360/c95527ac-c8f6-b5ff-814c-e6cd0488e495.jpeg)

## Wi-SUN通信デバイスを買う

また、知らない言葉が出てきました。

Wi-SUN とは、スマートメーターとの通信に使われている無線規格です。（スマートメーターに限らず、Wi-Fiでは不可能な低消費電力長距離高信頼な無線規格を目指した日本発の国際規格なんですが、いまは日本のスマートメーターだけで実用されている規格です。将来ガラパゴス云われないか心配です。）

ここでは、Wi-SUN通信デバイスとして ROHM の [BP35A1](http://www.rohm.co.jp/web/japan/news-detail?news-title=2015-01-07_ad&defaultGroupId=false) を利用します。一般人が買うことのできる最安Wi-SUNデバイスだと思われます。

![BP35A1withMathor.jpg](https://qiita-image-store.s3.amazonaws.com/0/104360/062f514f-6ffb-c8e0-8b97-c62be7e78166.jpeg)

この絵を見ると、マザーボードBP359Cが必要そうに見えますが、要らない機能満載なので不要です。

アダプタボードBP35A7Aは、言い方を変えると「ブレイクアウト基板」です。素人の電子工作に利用しやすい2.54mmピッチに変換してくれるヤツです。

アダプタボードBP35A7Aから、3.3Vレベルのシリアルポートが出ているので、それをラズパイなどのマイコンにつなぐか、USB-シリアル変換を通してPCにつなぐなどします。

### もっと簡単に
BP35A1と、USB-シリアル変換をまとめて内蔵したUSBドングル、[WSR35A1-00](http://www.rohm.co.jp/web/japan/news-detail?news-title=2014-10-02_news&defaultGroupId=false)もあります。15,000円（税抜き）です。

![WSR35A1-00.jpg](https://qiita-image-store.s3.amazonaws.com/0/104360/d32872a5-b264-5aa9-85ba-9f32131bb9ff.jpeg)

電子工作初心者にはこれがお手軽だと思います。USBポートに挿すだけなので、電子工作ではなくなってしまうという欠点（？）があります。

電子工作できる方は、BP35A1とアダプターボードとUSB-シリアル変換（もしくはラズパイのシリアルピン直結）で、ちょっと安く済ませることが可能でしょう。

## ハードウェア準備まとめ

- Bルートサービスの利用申し込みをする
- Wi-SUN通信デバイスを買う(下記のどっちか)
 - BP35A1 と アダプタボード(とコネクタ・ネジ(とUSBシリアル変換))
 - WSR35A1-00（オススメ）

それと、省略してましたが、母艦となるコンピュータも必要です。下のサンプルはpythonで書いたので、pythonが動くコンピュータならなんでも大丈夫だと思います。

# ソフトウェア

ソフトウェアを作るにあたって必要な資料が3つほどあります。この記事のサンプルを動かす分には見なくてもいいのですが、更なる改良をしていく場合は参照してみましょう。

## BP35A1の資料
[「ROHM Sub-GHzシリーズ」サポートページ](http://micro.rohm.com/jp/download_support/wi-sun/) の「BP35A1 コマンドリファレンス マニュアル」

WSR35A1-00を使う場合でも、中身はBP35A1なので「BP35A1 コマンドリファレンス マニュアル」を参照することになります。

## ECHONET Lite規格書
[ECHONET Lite規格書 Ver.1.12（日本語版）](https://echonet.jp/spec_v112_lite/)

5部まであってボリューム満点なのですが、さしあたっては「第2部 ECHONET Lite 通信ミドルウェア仕様」を見れば事足りるんじゃないかと思います。

## ECHONET機器オブジェクト詳細規定
[APPENDIX ECHONET機器オブジェクト詳細規定 Release H](https://echonet.jp/spec_object_rh/)

APPENDIX（付録）という割に本体よりでかくて付録商法かよ！状態ですが、いろんな機器の話が書いてあるためでかくなってます。目次を見るとありとあらゆる家電が網羅されててワクワクしますよね？

とりあえずはスマートメーター（低圧スマート電力量メータクラス規定）のところだけ見ればOKです。

## サンプルプログラム

プログラム作成の方針として

- 通信仕様の理解しやすさ優先
- エラー処理なし
- まっすぐ読めるようにユーティリティ関数を作らない

といったことを重視しています。お行儀の悪いプログラムになっています。

言語は Python 2.7 を使用しました。
シリアル通信には pyserial を使用しています。
ラズパイ(raspbian)の場合は、最初からPythonとpyserial が入っています（2014-06-02以降)

WindowsとMacでも動作チェック済みです。(Python + pyserial インストール済みであることが前提)

ご自分の環境に合わせて、ID、パスワードと、シリアルポートデバイス名の3か所を編集しましょう。

```python:bp35a1.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import print_function

import sys
import serial
import time

# Bルート認証ID（東京電力パワーグリッドから郵送で送られてくるヤツ）
rbid  = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# Bルート認証パスワード（東京電力パワーグリッドからメールで送られてくるヤツ）
rbpwd = "XXXXXXXXXXXX"
# シリアルポートデバイス名
serialPortDev = 'COM3'	# Windows の場合
serialPortDev = '/dev/ttyUSB0'	# Linux(ラズパイなど）の場合
serialPortDev = '/dev/cu.usbserial-A103BTPR'	# Mac の場合

# シリアルポート初期化
ser = serial.Serial(serialPortDev, 115200)

# とりあえずバージョンを取得してみる（やらなくてもおｋ）
ser.write("SKVER\r\n")
print(ser.readline(), end="") # エコーバック
print(ser.readline(), end="") # バージョン

# Bルート認証パスワード設定
ser.write("SKSETPWD C " + rbpwd + "\r\n")
print(ser.readline(), end="") # エコーバック
print(ser.readline(), end="") # OKが来るはず（チェック無し）

# Bルート認証ID設定
ser.write("SKSETRBID " + rbid + "\r\n")
print(ser.readline(), end="") # エコーバック
print(ser.readline(), end="") # OKが来るはず（チェック無し）

scanDuration = 4;   # スキャン時間。サンプルでは6なんだけど、4でも行けるので。（ダメなら増やして再試行）
scanRes = {} # スキャン結果の入れ物

# スキャンのリトライループ（何か見つかるまで）
while not scanRes.has_key("Channel") :
	# アクティブスキャン（IE あり）を行う
	# 時間かかります。10秒ぐらい？
	ser.write("SKSCAN 2 FFFFFFFF " + str(scanDuration) + "\r\n")

	# スキャン1回について、スキャン終了までのループ
	scanEnd = False
	while not scanEnd :
		line = ser.readline()
		print(line, end="")

		if line.startswith("EVENT 22") :
			# スキャン終わったよ（見つかったかどうかは関係なく）
			scanEnd = True
		elif line.startswith("  ") :
			# スキャンして見つかったらスペース2個あけてデータがやってくる
			# 例
			#  Channel:39
			#  Channel Page:09
			#  Pan ID:FFFF
			#  Addr:FFFFFFFFFFFFFFFF
			#  LQI:A7
			#  PairID:FFFFFFFF
			cols = line.strip().split(':')
			scanRes[cols[0]] = cols[1]
	scanDuration+=1

	if 7 < scanDuration and not scanRes.has_key("Channel"):
		# 引数としては14まで指定できるが、7で失敗したらそれ以上は無駄っぽい
		print("スキャンリトライオーバー")
		sys.exit()	#### 糸冬了 ####

# スキャン結果からChannelを設定。
ser.write("SKSREG S2 " + scanRes["Channel"] + "\r\n")
print(ser.readline(), end="") # エコーバック
print(ser.readline(), end="") # OKが来るはず（チェック無し）

# スキャン結果からPan IDを設定
ser.write("SKSREG S3 " + scanRes["Pan ID"] + "\r\n")
print(ser.readline(), end="") # エコーバック
print(ser.readline(), end="") # OKが来るはず（チェック無し）

# MACアドレス(64bit)をIPV6リンクローカルアドレスに変換。
# (BP35A1の機能を使って変換しているけど、単に文字列変換すればいいのではという話も？？)
ser.write("SKLL64 " + scanRes["Addr"] + "\r\n")
print(ser.readline(), end="") # エコーバック
ipv6Addr = ser.readline().strip()
print(ipv6Addr)

# PANA 接続シーケンスを開始します。
ser.write("SKJOIN " + ipv6Addr + "\r\n");
print(ser.readline(), end="") # エコーバック
print(ser.readline(), end="") # OKが来るはず（チェック無し）

# PANA 接続完了待ち（10行ぐらいなんか返してくる）
bConnected = False
while not bConnected :
	line = ser.readline()
	print(line, end="")
	if line.startswith("EVENT 24") :
		print("PANA 接続失敗")
		sys.exit()	#### 糸冬了 ####
	elif line.startswith("EVENT 25") :
		# 接続完了！
		bConnected = True

# これ以降、シリアル通信のタイムアウトを設定
ser.timeout = 2

# スマートメーターがインスタンスリスト通知を投げてくる
# (ECHONET-Lite_Ver.1.12_02.pdf p.4-16)
print(ser.readline(), end="") #無視

while True:
	# ECHONET Lite フレーム作成
	# 　参考資料
	# 　・ECHONET-Lite_Ver.1.12_02.pdf (以下 EL)
	# 　・Appendix_H.pdf (以下 AppH)
	echonetLiteFrame = ""
	echonetLiteFrame += "\x10\x81"		# EHD (参考:EL p.3-2)
	echonetLiteFrame += "\x00\x01"		# TID (参考:EL p.3-3)
	# ここから EDATA
	echonetLiteFrame += "\x05\xFF\x01"	# SEOJ (参考:EL p.3-3 AppH p.3-408～)
	echonetLiteFrame += "\x02\x88\x01"	# DEOJ (参考:EL p.3-3 AppH p.3-274～)
	echonetLiteFrame += "\x62"			# ESV(62:プロパティ値読み出し要求) (参考:EL p.3-5)
	echonetLiteFrame += "\x01"			# OPC(1個)(参考:EL p.3-7)
	echonetLiteFrame += "\xE7"			# EPC(参考:EL p.3-7 AppH p.3-275)
	echonetLiteFrame += "\x00"			# PDC(参考:EL p.3-9)
	
	# コマンド送信
	command = "SKSENDTO 1 {0} 0E1A 1 {1:04X} {2}".format(ipv6Addr, len(echonetLiteFrame), echonetLiteFrame)
	ser.write(command)

	print(ser.readline(), end="") # エコーバック
	print(ser.readline(), end="") # EVENT 21 が来るはず（チェック無し）
	print(ser.readline(), end="") # OKが来るはず（チェック無し）
	line = ser.readline()         # ERXUDPが来るはず
	print(line, end="")
	
	# 受信データはたまに違うデータが来たり、
	# 取りこぼしたりして変なデータを拾うことがあるので
	# チェックを厳しめにしてます。
	if line.startswith("ERXUDP") :
		cols = line.strip().split(' ')
		res = cols[8]	# UDP受信データ部分
		#tid = res[4:4+4];
		seoj = res[8:8+6]
		#deoj = res[14,14+6]
		ESV = res[20:20+2]
		#OPC = res[22,22+2]
		if seoj == "028801" and ESV == "72" :
			# スマートメーター(028801)から来た応答(72)なら
			EPC = res[24:24+2]
			if EPC == "E7" :
				# 内容が瞬時電力計測値(E7)だったら
				hexPower = line[-8:]	# 最後の4バイト（16進数で8文字）が瞬時電力計測値
				intPower = int(hexPower, 16)
				print(u"瞬時電力計測値:{0}[W]".format(intPower))
				


# 無限ループだからここには来ないけどな
ser.close()

```

プログラムの詳細についてはコメントを見てください。

これを実行すると、以下のように出力されます。
理解促進のためのサンプルなので、通信内容がうるさく出ています。

```
$ ./bp35a1.py
SKVER
EVER 1.2.10
OK
SKSETPWD C XXXXXXXXXXXX
OK
SKSETRBID XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
OK
SKSCAN 2 FFFFFFFF 4
OK
EVENT 22 FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX
SKSCAN 2 FFFFFFFF 5
OK
EVENT 20 FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX
EPANDESC
  Channel:39
  Channel Page:09
  Pan ID:XXXX
  Addr:XXXXXXXXXXXXXXXX
  LQI:95
  PairID:XXXXXXXX
EVENT 22 FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX
SKSREG S2 39
OK
SKSREG S3 XXXX
OK
SKLL64 XXXXXXXXXXXXXXXX
FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX
SKJOIN FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX
OK
EVENT 21 FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX 00

～中略～

SKSENDTO 1 FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX 00
OK
ERXUDP FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX 0E1A 0E1A XXXXXXXXXXXXXXXX 1 0012 1081000102880105FF017201E704000002AE
瞬時電力計測値:686[W]
SKSENDTO 1 FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX 00
OK
ERXUDP FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX FE80:0000:0000:0000:XXXX:XXXX:XXXX:XXXX 0E1A 0E1A XXXXXXXXXXXXXXXX 1 0012 1081000102880105FF017201E7040000029B
瞬時電力計測値:667[W]

～くりかえし～

```

最初のネゴに10秒ぐらい時間がかかります。また、リクエストを投げて、1秒ぐらい経ってからレスポンスが来るようです。「リアルタイム」「瞬時値」という割にはちょっと遅いかなーという印象です。

# おわりに

スマートメーターと通信するためのハードウェア BP35A1 の紹介と、それを利用するソフトウェアの最小限のサンプルを示しました。
標準出力に出力するだけのプログラムで見た目がショボイのですが、GUIのためなどの余計なコードがなく、通信仕様を理解するには役に立ったのではないかと思います。

スマートメーターは様々な機器と繋げてこそ活用できるものですので、一般ユーザーが生情報にアクセスできるのは大変価値のあることだと思います。
この記事をもとに、おうちハックへの応用につなげていただけたら幸いです。

## 追記1

参考にしたサイトを省略してしまいました。すいません。

[スマートメーターの瞬時電力や履歴をWebブラウザで見る](http://d.hatena.ne.jp/NeoCat/20160117/1453021993)

[スマートメーターとRaspberry Piで電力計を作った]
(http://chappnet.hateblo.jp/entry/2015/08/23/112738)

## 追記2

ERXUDP（とERXTCP)のレスポンス形式がデフォルトではバイナリなのですが、扱いにくいので16進ASCIIに変更しておく必要があります。

サンプルのPythonコードを動かす前に、**TeraTermなどで WOPT 01 コマンド送信しておきましょう。**

このコマンドを打った瞬間に内蔵フラッシュに書き込みに行くので、プログラムから起動時に送信することはお勧めできません。
プログラムに組み込む場合は、ROPTコマンドで現在の設定値を読み取って、変更が必要な場合のみ WOPT 01 コマンドを送信するようにしましょう。

## 追記3

USBドングル WSR35A1-00 の15,000円、もしくはBP35A1と周辺合わせて1万円弱という値段が安いのか高いのかというのは議論のあるところでしょう。
キャッチーなタイトルを付けておきながらアレですが、個人的にも正直、高いと思います。もっと数が出るデバイスになれば、WiFiドングルぐらいの値段（約1000円）に近づくことができるんじゃないかなーと思っています。
もっと安いデバイスをご存知でしたら、コメントなどでお知らせいただくと幸いです。
（フレッツミルエネの5000円のUSBドングルがあるみたいですが、仕様が分からないのでフレッツミエルネを契約しないと使えなさそうです。）

## 追記4

テセラ・テクノロジー株式会社から [RL7023 Stick-D/IPS](http://www.tessera.co.jp/rl7023stick-d_ips.html)  という製品がリリースされています。[chip1stop](http://www.chip1stop.com/dispDetail.do?partId=TSRA-0000042)から8,060円（税抜）で購入することができます。
通信仕様はWSR35A1-00やBP35A1と同じみたいです。上記プログラムがそのまま動くことを確認しました。
小さいし安いので、現在のオススメはコチラになります。

![DSC_0447.JPG](https://qiita-image-store.s3.amazonaws.com/0/104360/8d9b91ed-d388-1e37-59d6-75b11369a757.jpeg)

## 追記5

M5StickC と BP35A1 を簡単につなげるための Wi-SUN HATキット が[BOOTH](https://booth.pm)で販売されています。
[M5StickC用「Wi-SUN HAT」キット](https://kitto-yakudatsu.booth.pm/items/1650727)
ESP32系を使いたい場合は、この組み合わせがベストバイだと思います。
（ラズパイなどUSB搭載機器で使いたい場合は、前項のRL7023 Stick-D/IPS がいいと思います）

## 追記6

BP35A1 は終売のようです。

「最安」というところでは、秋月電子の[部品取りに!無線モジュール付きSoc基板+白色プラスチックケース](https://akizukidenshi.com/catalog/g/g117437/) 2,200円 というモノがあります。母艦となるコンピューター付きなので超絶オトクです。しかし、環境構築が複雑なのと、在庫限りであることが難点です。
参考：[秋月電子の謎SOC基板を使って、家の電力計の値を取得してHome Assistantで可視化しよう](https://qiita.com/tonasuzuki/items/40fa0d722f7468ec79f9)