電気スマートメーターに替えて自作で電気の見える化装置製作

アナログメーター Home Assistant・IoT

ウチの電気メーターをスマートメーターに変更してもらったら、Raspberry Pi(ラズパイ)とWi-SUN通信できるUSBモジュールを使って瞬時電力と積算電力をハックして、激安で電気の見える化をする方法の続きです。

この記事は複数のページに別れています

更新するのを忘れていて・・・遅くなってしまってスンマセンm(_ _)m

ちなみにPythonは詳しくなく、適当に改造しただけなのでバグやうまく動かないことが多々あると思います・・・おかしいところがありましたらご指導いただけると大喜びします♬

Wi-SUN USBドングルのRL7023のデバイス名を固定

まずはラズパイでUSBドングルのRL7023のデバイス名を固定するところから始めていきましょう。

USBデバイス名を固定する方法の詳しくは以下をお読みください。

【自作ソーラー発電 DIY】窓ガラス室内設置でMax19V4A76Wほどの発電で満足・その後バッテリーヘタリ
ソーラー発電の準備から接続、ディープサイクルバッテリーに蓄電をまとめました。 ソーラーパネルの設置場所は部屋の窓内なのでそれほどの発電はしませんでしたが、思ったよりも発電して、自作ソーラー発電の初めの一歩としては満足です。

ttyUSBを調べてみると以下の通り↓

$ ls /dev/ttyUSB*
/dev/ttyUSB0  /dev/ttyUSB1  /dev/ttyUSB_solar

HEMS用 Wi-SUNモジュール RL7023は、ttyUSB1にマウントされていました。この状態だと他のUSBデバイスが接続されるたびにデバイス名がttyUSB1・ttyUSB2・ttyUSB3などに変わってしまうので、デバイス名を固定していきます。

Vendor・Productを取得

まずはlsusbコマンドでRL7023のVendorとProductを調べていきます。

$ lsusb
Bus 001 Device 004: ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter
Bus 001 Device 006: ID 0bda:0109 Realtek Semiconductor Corp.
Bus 001 Device 007: ID 0403:6015 Future Technology Devices International, Ltd Bridge(I2C/SPI/UART/FIFO)
Bus 001 Device 005: ID 0424:7800 Standard Microsystems Corp.
Bus 001 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

QinHeng Electronics HL-340はチャージコントローラーのケーブル、Realtek Semiconductor Corp.はMicroUSBを刺したUSBアダプタ。

該当のRL7023は、ID 0403:6015 Future Technology Devices International, Ltdになっていました。

  • idVendor:0403
  • idProduct:6015
  • メーカー:Future Technology Devices International, Ltd

このように取得できましたので、デバイス名/dev/ttyUSB1 を /dev/ttyUSB_powerに固定していきます。

固定デバイス情報を99-com.rulesに書く

venderとProductoの文字を入れて、シンボリックリンク名は「ttyUSB_power」にしました。

$ sudo vim /etc/udev/rules.d/99-com.rules
#以下を追加します。
KERNEL=="ttyUSB*", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", SYMLINK+="ttyUSB_power"

これで再起動してもデバイス名が変わらず、必ず/dev/ttyUSB_powerを見に行きますのでエラーになることはありません。

ラズパイで電気スマートメーターと通信するための下準備

電気スマートメーターと通信するWi-SUNドングルのRL7023をラズパイで使えるようにいろいろとインストールしていきます。

pyserial

pyserialが必要なのでインストールする。

$ cd /tmp
$ sudo curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
$ sudo python get-pip.py
$ sudo pip install pyserial

電力データを保存するDB InfluxDB

電気スマートメーターから取得した電力データを保存するためのデータベースです。

インストールや設定などの詳しくはコチラ↓をご覧ください。

【Iot環境センシング下準備】InfluxDB設定+Grafana+telegrafでラズパイデータ可視化 カッコいいグラフを作ろう
自宅環境センシングの2回目の記事として今回はラズパイ(Raspberry Pi)にデータベースのInfluxDBを入れて将来的にセンシングしたデータをInfluxDB保存していきます。 そして次回は、GrafanaというデータをWebで美し
【Iot環境センシング下準備】InfluxDB設定+Grafana+telegrafでラズパイデータ可視化 カッコいいグラフを作ろう
自宅環境センシングの2回目の記事として今回はラズパイ(Raspberry Pi)にデータベースのInfluxDBを入れて将来的にセンシングしたデータをInfluxDB保存していきます。 そして次回は、GrafanaというデータをWebで美し...
$ sudo pip install influxdb

他のMySQLなどのデータベースやクラウドに保存される方はInflaxDBは導入する必要はありません。

Pandas

InfluxDBのResultを取り出すときに使いますので、InflaxDBを使わない場合は必要ありません。

$ sudo pip install pandas

電気スマートメーターの電力データを取得してみる

この方のプログラムを改造させていただきました。ありがとうございますm(_ _)m

スマートメーターの情報を最安ハードウェアで引っこ抜く - Qiita
#はじめに電力自由化にともない、スマートメーターの設置が進められています。電力会社が設置したスマートメーターと、各社から販売されているHEMS見える化端末的なモノを導入すると消費電力などをリアル…
M5Stackでスマートメーターの電力量を取得してAmbientでグラフ表示 - Qiita
はじめに前回の記事M5Stackでスマートメーターの瞬時電力をBP35A1から取得して表示してみたの続きです。今回は瞬時電力量, 定時積算電力量計測値 をBP35A1経由で取得して、Amb…

まずはテスト的にターミナルからコチラ↑からいただいたスクリプトを動かしてみます。

以下のように電気スマートメーターとWi-SUN通信デバイスが通信しました。

$ python power.py
SKVER
EVER 1.2.8
OK
SKSETPWD C VL0BXEG5HQ7C
OK
SKSETRBID 000000BF7706672ECC7321273541992E
OK
SKSCAN 2 FFFFFFFF 4
OK
EVENT 22 FE80:0000:0000:0000:1207:23FF:FEA0:74C5
SKSCAN 2 FFFFFFFF 5
OK
EVENT 20 FE80:0000:0000:0000:1207:23FF:FEA0:74C5
EPANDESC
  Channel:3B
  Channel Page:09
  Pan ID:2BD8
  Addr:38AFD7FFFE2A7189
  LQI:28
  PairID:3541992E
EVENT 22 FE80:0000:0000:0000:1207:23FF:FEA0:74C5
SKSREG S2 3B
OK
SKSREG S3 2BD8
OK
SKLL64 38AFD7FFFE2A7189
FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189
SKJOIN FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189
OK
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 02
EVENT 02 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189
EVENT 01 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 02CC 02CC 38AFD7FFFE2A7189 0 0028 00000028C00000028224BEA3B9C2315100060000000400000000000500030000000400000000000C
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 02CC 02CC 38AFD7FFFE2A7189 0 0068 00000068800000028224BEA3B9C231520005000000100000212F98B7587A10146E81077D23382AB80002000000380000016500382F008A9048003068EB3B4B694C7A22DE003D534D3030303030304246373730363637324543433733323132373335343139393245
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 02CC 02CC 38AFD7FFFE2A7189 0 0054 00000054800000028224BEA3B9C2315300020000003B00000166003B2F808A9048003068EB3B4B694C7A22DE003DA6246BC411C3F4DFA0F738038D3079FF00000000F84958A16045B78BFCA3F75D9C9953064500
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 02CC 02CC 38AFD7FFFE2A7189 0 0058 00000058A00000028224BEA3B9C2315400070000000400000000000000020000000400000366000400040000000400000000000100080000000400000001518000010000001000005BE4275B1CD6E2B485D84173C008071D
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
EVENT 25 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FF02:0000:0000:0000:0000:0000:0000:0001 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 108100960EF0010EF0017301D50401028801
SKSENDTO 1 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FF02:0000:0000:0000:0000:0000:0000:0001 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 108100970288010EF0017302810161880142
SKSENDTO 1 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 1081000102880105FF017201E70400000256
瞬時電力計測値:598[W]
SKSENDTO 1 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 1081000102880105FF017201E70400000256
瞬時電力計測値:598[W]
SKSENDTO 1 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 1081000102880105FF017201E70400000256
瞬時電力計測値:598[W]
SKSENDTO 1 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 1081000102880105FF017201E70400000256
瞬時電力計測値:598[W]
SKSENDTO 1 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 1081000102880105FF017201E70400000240
瞬時電力計測値:576[W]
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 1081000102880105FF017201E70400000240
SKSENDTO 1 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
SKSENDTO 1 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 1081000102880105FF017201E70400000240
瞬時電力計測値:576[W]
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 1081000102880105FF017201E70400000240
SKSENDTO 1 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
SKSENDTO 1 FE80:0000:0000:0000:ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 1081000102880105FF017201E70400000208
3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
SKSENDTO 1 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
ERXUDP FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 FE80:0000:0000:0000:1207:23FF:FEA0:74C5 0E1A 0E1A 38AFD7FFFE2A7189 1 0012 1081000102880105FF017201E70400000208
瞬時電力計測値:520[W]
SKSENDTO 1 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 0E1A 1 000E
EVENT 21 FE80:0000:0000:0000:3AAF:D7FF:FE2A:7189 00
OK
・・・省略

瞬時電力計測値が永遠と取得できています。このスクリプトを改造して、瞬時電力計測値だけではなく積算電力量計測値や瞬時電流(A)などの値も取得していきます。

ECHONET エコーネット規格を調査

ここで電気スマートメーターとUSBドングルのRL7023がどのような通信をするのか、エコーネット規格を参照して簡単に調査していきます。

エコーネット規格 規格書

電気スマートメーターの通信規格の説明書↓を参考に、どうやって数値を取得していくか勉強していきます。

エコーネットコンソーシアム
豊かな未来の社会を実現するために。エコーネットコンソーシアム

参考にしたのは以下の2つのPDFファイル

  • APPENDIXECHONET機器オブジェクト詳細規定
    • Release N 最新 2020 9/25
    • Release H 旧版
  • 第2部 ECHONET Lite 通信ミドルウェア仕様 ECHONET-Lite_Ver.1.13_02.pdf

電気以外にも色々とあって長すぎて見るのがメンドウです・・・

コチラ↓の方の表も参考にさせていただきました。ありがとうございますm(_ _)m

HEMS スマートメーターからの出力
電灯契約を変えたことで、スマートメータに取り替えられた。 東電にBルートサービスを申し込んで、自宅raspberry pi3に テセラ・テクノロジーのRL7023 Stick-D/IPS を刺して計測...

HEMS 電気スマートメーターから取得できる電力値

電気スマートメーターから取得できる値を調べてみた結果は以下のようになりました。

個人的に調べた結果で自分の能力不足な点が多々ありそうで理解できない値がありました。もっと知識のある方はちゃんと取得できるのかもしれません。

APPENDIXECHONET機器オブジェクト詳細規定 Release N 最新の3-307ページ「3.3.25低圧スマート電力量メータクラス規定」あたりを参考に値を調査・取得してみました。

送られてくるデータは16進数なので10進数に変換して意味を理解してみます。意味が分からない箇所「?」の部分は未解決。

EPC プロパティ名称 取得データ 内容
D3 係数 00000001 1
D7 積算電力量有効桁数 06 6桁
E0 積算電力量計測値(正方向計測値) 00022721 14108.9kWh
E1 積算電力量単位(正方向、逆方向計測値) 01 0.1kWh
E2 積算電力量計測値履歴1(正方向計測値) 00FFFFFFFFFEFFFFFFFE・・・省略 ??
E3 積算電力量計測値(逆方向計測値) FFFFFFFEE304FFFFFFFE 4294967294
E4 積算電力量計測値履歴1(逆方向計測値) 00FFFFFFFFFEFFFFFFFE・・・省略 ??
E5 積算履歴収集日1 FFE501 255
E7 瞬時電力計測値 000000CE 206W
E8 瞬時電流計測値 忘れた・・・
EA 定時積算電力量正方向 メーターの数値 7E30C060A00000002276C EA0B 07E3 2019年 0C=12月 06=6日 0A=10時 00=0分 00=0秒 0002276C=141164
EB 定時 積算電力量 計測値 (逆方向計測値) 7E30C06100000FFFFFFFE EB0B 07E3 2019年 0C=12月 06=6日10=16時 00 00 FFFFFFFE=4294967294
EC 積算電力量計測値履歴2(正方向、逆方向計測値) 取得できない ウチのスマートメーターには無い
ED 積算履歴収集日2 取得できない ウチのスマートメーターには無い

このうち以下の6項目を取得できたので、今回は6項目だけをランダムに取得していきます。

  • E0:積算電力量計測値(正方向計測値)
  • E1:積算電力量単位(正方向、逆方向計測値)
  • E3:積算電力量計測値(逆方向計測値)
  • E7:瞬時電力計測値
  • E8:瞬時電流計測値
  • EA:定時積算電力量計測(正方向計測値)

電気見える化のPythonプログラム

電気スマートメーターから取得した瞬時電流などの値をPythonで取得して、InflaxDBに保存するスクリプトです。

電気スマートメーターのデータを取得するpower_rand.py

以下、コチラのスクリプトを改造してE0、E1、E3、E7、E8、EAをランダムに取得するスクリプトです。

pip3 install git+https://github.com/AmbientDataInc/ambient-python-lib.git

Cronで一定時間ごとに動かして、無限ループなので一定時間ごとにpkillしてます。自分の環境では一応動いていますが、もっと良い方法などありましたらご指導しただけると喜びます♪

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import print_function

import sys
import serial
import time
import random
import ctypes #unit32

#------------設定ココから------------------------
#influxDB
from influxdb import InfluxDBClient
client = InfluxDBClient(host='192.168.31.53', port=8086, username='root', password='パスワード', database='sensor')
measurement = 'power'
tags = {'place': 'umi','host': 'RL7023'}

# Bルート認証ID
rbid  = "BルートでもらったID"
# Bルート認証パスワード
rbpwd = "パスワード"
# シリアルポートデバイス名
serialPortDev = '/dev/ttyUSB_power' 
#------------設定ココまで------------------------ 

# シリアルポート初期化
ser = serial.Serial(serialPortDev, 115200)
# とりあえずバージョンを取得してみる(やらなくてもおk)
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 = {} # スキャン結果の入れ物

#influxDBにアップロード
def influx_json(field,nowtime=''):
	json_body = [
		{
			'measurement': measurement,
			'tags': tags,
			'fields': field
			#'time': nowtime
		}
	]
	client.write_points(json_body)
	print(json_body)
	#sys.exit()

# スキャンのリトライループ(何か見つかるまで)
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="") #無視

#powersの配列でランダムに取得していく。
#powersの配列を変えると他の値も取得できるかもしれませんのでやってみてください。
#特にソーラーパネルを導入して売電している方は逆方向計測値も取得したほうが良さそうです。
#powers = ["\xD3","\xD7","\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE7","\xE8","\xEA","\xEB"]
#powers = ["\xE0","\xE1","\xE2","\xE3","\xE4","\xE5","\xE7","\xE8","\xEA","\xEB"]
powers = ["\xE0","\xE1","\xE3","\xE7","\xE8","\xEA"]
#for echonet in powers:#配列を一つずつ取得していっても何故か?途中で終わるのでランダムで取得する
while powers:
	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)

	randp = random.choice(powers)
	echonetLiteFrame += randp
	print(randp)

	echonetLiteFrame += "\x00"
	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受信データ部分
		seoj = res[8:8+6]
		ESV = res[20:20+2]
		#print(ESV)

		if seoj == "028801" and ESV == "72" :
			EPC = res[24:24+2]
			print(EPC)

			if EPC == "D3" :#係数 D304 00000001=1
				print(EPC)
			elif EPC == "D7" :#積算電力量有効桁数 D701 06=6桁
				print(EPC)

			elif EPC == "E0" :#積算電力量計測値(正方向計測値) E004 00022721=14108.9 0002272A=14109.8kWh
				hexPower = line[-8:]	# 最後の4バイト(16進数で8文字)が瞬時電力計測値
				intPower = int(hexPower, 16)
				print(EPC)
				print(u"sekisan{0}[kWh]".format(intPower))
				field = {'E0': float(intPower)}
				influx_json(field)

			elif EPC == "E1" :#積算電力量単位(正方向、逆方向計測値) E10101=0.1kWh
				hexPower = line[-4:]	# 最後の4バイト(16進数で8文字)
				intPower = int(hexPower, 16)
				print(EPC)
				print(u"sekisan seigyaku{0}[kWh]".format(intPower))
				field = {'E1': float(intPower)}
				influx_json(field)

			elif EPC == "E2" :#積算電力量計測値履歴1(正方向計測値)  ???
				print(EPC)

			elif EPC == "E3" :#積算電力量計測値(逆方向計測値) E304 FFFFFFFE=4294967294
				hexPower = line[-8:]	# 最後の4バイト(16進数で8文字)
				intPower = int(hexPower, 16)
				print(EPC)
				print(u"sekisan Reverse{0}[kWh]".format(intPower))
				field = {'E3': float(intPower)}
				influx_json(field)

			elif EPC == "E4" :#積算電力量計測値履歴1(逆方向計測値) ???
				print(EPC)
			elif EPC == "E5" :#積算履歴収集日1:E501FF
				print(EPC)

			elif EPC == "E7" :# 瞬時電力計測値:E704 000000CE=206W
				hexPower = line[-8:]	# 最後の4バイト(16進数で8文字)が瞬時電力計測値
				intPower = int(hexPower, 16)
				print(EPC)
				print(u"power:{0}[W]".format(intPower))
				field = {'power': float(intPower) }
				influx_json(field)

				#txtファイルに出力
				with open('/home/pi/e7.txt', 'w') as f:
					print(float(intPower), file=f)

			elif EPC == "E8" :#瞬時電流(A) Current458792 最後の2バイト2個
				hexCurR = line[-8:-4]
				hexCurT = line[-4:]
				intCurR = int(hexCurR, 16)
				intCurT = int(hexCurT, 16)
				intCurR = float(intCurR)
				intCurT = float(intCurT)
				intCurR = intCurR / 100
				intCurT = intCurT / 100

				print(EPC)
				#print(hexCurR)
				print(u"R:{0}[A]".format(intCurR)) #1280 153
				print(u"T:{0}[A]".format(intCurT)) #54 97 32 61
				field = {'E8_R': intCurR , 'E8_T': intCurT }
				influx_json(field)

			elif EPC == "EA" :#定時積算電力量正方向 メーターの数値 EA0B 07E3=2019年 0C=12月 06=6日 0A=10時 00=0分 00=0秒 0002276C=14116
				hexYearKW = line[-22:-18]
				hexMoonKW = line[-18:-16]
				hexDayKW = line[-16:-1]
				hexTimeKW = line[-8:]	# 最後の4バイト(16進数で8文字)
				nowtime = '{} {}:{}:{}'.format(hexYearKW, hexMoonKW, hexDayKW, hexTimeKW)
				print(u"Now {0}".format(nowtime))
				intTimeKW = int(hexTimeKW, 16)#kWh

				print(EPC)
				print(u"teizi{0}[kWh]".format(intTimeKW))
				field = {'EA': float(intTimeKW) }
				#influx_json(field,nowtime)
				influx_json(field)

			elif EPC == "EB" :#積算履歴収集日1:EB0B07E30C050B1E00FFFFFFFE
				print(EPC)
			elif EPC == "EC" :#積算電力量計測値履歴2(正方向、逆方向計測値) ウチのスマートメーターには無い
				print(EPC)
			elif EPC == "ED" :#積算履歴収集日2 ウチのスマートメーターには無い
				print(EPC)
			else :
				print("Other")
				print(EPC)

time.sleep(1)

ファイルでもダウンロードできるようにしておきました。↓(右クリックして「リンク先を保存」を選択してダウンロードしてください。)

power_rand.py

データベースにアップロードする電力の値

取得するデータは6項目でE1・E3は賃貸マンションで売電していない自分の場合は特に数値が変化することはありませんでしたが、数値が一定ですが一応データは送られてきます。

InflaxDB以外のデータベースやクラウドを使う場合は、influx_json関数でInflaxDBにアップロードしていますので、コチラを変更して、各プロパティのinflux_json(field)の部分も変更してみてください。

電気スマートメーターから送られてきた16進数データを10進数にしてわかるようにしたデータ名は以下の通りです。

  • E0:E0(積算電力量計測値(正方向計測値))
  • E1:E1(積算電力量単位(正方向、逆方向計測値))売電で使う?
  • E3:E3(積算電力量計測値(逆方向計測値))売電している人は必要
  • E7:power(瞬時電力計測値)
  • E8:E8_R(電流R)、E8_T(電流T)
  • EA:EA(定時積算電力量正方向 メーターの数値)

Grafana用 電力JSON

Grafanaをお使いの場合は以下のJSONをインサートすると手っ取り早いです。(右クリックして「リンク先を保存」を選択してダウンロードしてください。)

https://bey.jp/wp-content/uploads/download/power.json

インサート方法はコチラ↓

【Iot環境センシング下準備】InfluxDB設定+Grafana+telegrafでラズパイデータ可視化 カッコいいグラフを作ろう
自宅環境センシングの2回目の記事として今回はラズパイ(Raspberry Pi)にデータベースのInfluxDBを入れて将来的にセンシングしたデータをInfluxDB保存していきます。 そして次回は、GrafanaというデータをWebで美し...

Cron

最後にCronで定期的にスクリプトを実行していきます。

piユーザーで以下のようにCronを設定します。

pi@raspi3B:~ $ crontab -e
#無限ループなので10分置きに終了させる
*/10 * * * * pkill -f power_rand.py > /home/pi/power_base_rand.log 2>&1
#D7やE7: 瞬時電力計測値・E8: 瞬時電流(A)などをランダムに取得する
3-58/5 * * * * /usr/bin/python /home/pi/script/power_rand.py > /home/pi/power_rand.log 2>&1

#再起動時に瞬間電力を表示
@reboot /usr/bin/python /home/pi/script/power_disp.py > /home/pi/power_disp.log 2>&1

power_disp.pyはラズパイに付けたちっちゃなディスプレイに瞬時電力を5付の機に更新さえて表示させています。

コチラ↓もPythonスクリプトをダウンロードできるようにしておきました。適当過ぎますけど・・・もしよかったらお使いください。(右クリックして「リンク先を保存」を選択してダウンロードしてください。)

https://bey.jp/wp-content/uploads/download/power_disp.py

power_rand.pyを実行すると、 Wi-SUN通信がおかしいのか?うまく接続できずにエラーになる場合↓もあります。接続できてもデータを取得できない場合もあってなんだか不安定です。

$ /usr/bin/python /home/pi/script/power_rand.py
Traceback (most recent call last):
  File "/home/pi/script/power_rand.py", line 70, in 
    line = ser.readline()
  File "/usr/lib/python2.7/dist-packages/serial/serialposix.py", line 490, in read
    'device reports readiness to read but returned no data '
serial.serialutil.SerialException: device reports readiness to read but returned no data (device disconnected or multiple access on port?)

Wi-SUN通信は?定期的にPythonスクリプトを実行しないとうまく接続できないようでした。

電気 見える化で電力グラフを作る まとめ

使用している電力会社が電気スマートメーターに替えて(自分で申請しても無料で替えてくれます。)くれたら、通常はHEMSを導入して電気の見える化ができますが、高額なHEMS機器を導入しなくても、Wi-SUN通信ができるUSBドングルとラズパイを使ってハックすれば簡易的に電気見える化のグラフを作ることができました。

今回の「自作 電気の見える化」のやり方は、電力会社に電気スマートメーターに替えてもらったら以下の準備をします。

ハードウェア
  • Linux:自分はRaspberry Pi 3Bを使いました。
  • Wi-SUN機器:USBドングル RL7023
Linux(ラズパイ)で使うソフト
  • InflaxDB:電気スマートメーターから取得した電力値を保存するデータベース
  • Grafana:InflaxDBに保存してあるデータをグラフ表示してくれるアプリ
  • Python:電力を取得してInflaxDBに保存するプログラムを作成
  • Cron:作成したPythonプログラムを定期的に実行・停止する

市販のHEMSに表示されるパネルには劣りますが、瞬時電力や積算電力が以下のようにグラフ表示させることができました。

電気スマートメーター

プログラムをもう少し進化させて、毎月の料金や毎日の電気使用量を計算できたら最高ですね。

けど、九州電力のページ見れば表示されるので必要ないかなぁ・・・

この記事は複数のページに別れています

コメント

  1. onusay より:

    pythonの基礎から勉強中です。

    def influx_json(field,nowtime=”):
    の終わりはどこでしょうか?
    returnは不要なのでしょうか?

    • GO より:

      onusay様
      コメントありがとうございます。
      自分もPythonは詳しくないので、ちゃんとお答えできるか不安ですが・・・
      client.write_points(json_body)の部分でデーターをInfluxに送信しています。
      なので、返り値(return)などは不要です。
      Pythonはインデント(タブやスペース)で一つのブロックと認識されるようにできているので、PHPなどの他の言語のように{}は不要です。