Home Assistant:電力・ガス・水道 をデータ化

Home Assistant・IoT

電力はスマートメーターに取り替えて正常にデータを受信できましたが、問題はガスと水道メーターをどうやってHome Assistantに取り込むかを考えていたら、すでにありました。

アナログな方法ですが、ESP32-CAMを使って定期的にメーターの画像から数値を抜き出してデータにする方法です。こちらはデータをちゃんと取得できない時があって不安定でした。

電気・ガス・水道のメーターをHome Assistantにデータとして取り込んでみましたので紹介します。

電気スマートメーターのデータをHome Assistantで表示する

結果として、電気スマートメーターだけが正確にメーターのデータを取得できるようになりました。

いつのまにか動かなくなって、しばらく放置しておいたHEMS用 Wi-SUNモジュールRoute-B 専用 RL7を再度動くようにしました。

ラズパイや他のLinuxがPyhton2(パイソン2)からPython3に完全に変わって動かなくなっていた。

それと、RL7が受信したりしなかったりと不安定だったので、電気スマートメーターに近い寝室のRaspberry Pi3BにUSBドングルをさして使うようにしたら、スマートメーターにアクセスしやすくなった。(今まで接続が不安定で苦労した。最初からスマートメーターの近くのラズパイでUSBドングル使えばよかった。)

設定やPythonスクリプトは以下のページを参考にさせていただきました。

GitHub - nullsnet/esp32-broute2mqtt-smartmeter
Contribute to nullsnet/esp32-broute2mqtt-smartmeter development by creating an account on GitHub.

まずはPython2からPython3のスクリプトにするために改造やMQTTのライブラリなどをインストールしていきます。

Home Assistantでデータを取得できるようにMQTTの設定

paho-mqttをインストール

Wi-SUNモジュールをさしてあるラズパイで操作します。

InfluxDBは1.0系を引き続き使って、Home AssistantではMQTTで送信するようにしたのでpipで以下を追加する。

sudo pip3 install paho-mqtt

Home AssistantのMQTTからUser/Passを取得

こちらはHome Assistantをインストールしてあるラズパイや自分の場合はKhadas VIM1Sで設定します。すでにMQTTを統合から追加している状態での作業。

設定 > デバイスとサービス >MQTTからユーザー名とパスワードを取得します。

MQTTの画面で↓設定を選んで。

MQTTの再設定を押すとBrokerオプションが表示される。

このユーザー名とパスワードをメモしましょう。ユーザー名は自動的に「homeassistant」になるようです。

Python3スクリプト

HEMS用 Wi-SUNモジュールRoute-B 専用 RL7が設置してあるラズパイのpower_mqtt.pyを作成しました。

Python3用でMQTT(Home Assistant用)とInfluxDBのサーバーに送信します。

電力データはEA・E7・E8(R・T)の4種類を取得

定期的にアクセスできなくなる(原因不明)のでタイムアウトした場合は再度初期化してからアクセスするようにしましたが、4日に1回ほどうまく取得できなくなる場合がある。(なんで?)

その場合は、sudo systemctl stop smart_meter.serviceしてstart/enableをするしかなさそう。(未解決)

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

import serial
import threading
import sys
import datetime
import time
import itertools
from influxdb import InfluxDBClient
import paho.mqtt.client as mqtt

# MQTTの設定
broker = '192.168.31.51'
port = 1883
username = 'homeassistant'
password = 'MQTTのパスワード'
#かぶらないように一意のIDにするランダムな名前
client_id = "power_mqtted9endyhdzde8"

# paho-mqttのバージョンが2.0.0にアップグレードされたため VERSION1,3どちらかを指定する。
def  connect_mqtt():
    client = mqtt.Client(clean_session=False, callback_api_version=mqtt.CallbackAPIVersion.VERSION2, client_id=client_id)
    client.username_pw_set(username, password)
    client.connect(broker, port, 60)
    return(client)

# InfluxDBの設定
client = InfluxDBClient(host='192.168.31.53',
                        port=8086,
                        username='root',
                        password='Pass',
                        database='sensor')
measurement = 'power'
tags = {'place': 'Power', 'host': 'RL7023'}

def influx_json(fields, nowtime=''):
    json_body = [
        {
            'measurement': measurement,
            'tags': tags,
            'fields': fields,
        }
    ]
    client.write_points(json_body)

# スマートメーターの設定
# BルートのID
routeBId = "BルートのID"
# Bルートのパスワード
routeBPwd ="Bルートのパスワード"
# シリアルデバイス:ttyUSB_powerに固定した。
serialPortDev = '/dev/ttyUSB_power'

def waitOK(serial_port):
    while True:
        line = serial_port.readline()
        print(line)
        if line.startswith(b"OK"):
            break

def initialize():
    # シリアルの初期化
    serial_port = serial.Serial(port=serialPortDev, baudrate=115200)

    for _ in range(3):  # 最大3回再試行
        # Bルートのパスワードを設定
        serial_port.write(("SKSETPWD C " + routeBPwd + "\r\n").encode())
        t = threading.Thread(target=waitOK, args=(serial_port,))
        t.start()
        t.join(timeout=60)
        if not t.is_alive():
            break  # 成功したらループを抜ける
        time.sleep(5)  # 少し待ってから再試行
    else:
        # タイムアウトした場合は例外を発生させる
        raise TimeoutError("waitOK timed out")

    for _ in range(3):  # 最大3回再試行
        # BルートのIDを設定
        serial_port.write(("SKSETRBID " + routeBId + "\r\n").encode())
        t = threading.Thread(target=waitOK, args=(serial_port,))
        t.start()
        t.join(timeout=60)
        if not t.is_alive():
            break  # 成功したらループを抜ける
        time.sleep(5)  # 少し待ってから再試行
    else:
        # タイムアウトした場合は例外を発生させる
        raise TimeoutError("waitOK timed out")


    # ネットワークのスキャン
    scanDuration = 5
    scanRes = {}
    while 'Channel' not in scanRes.keys():
        serial_port.write(("SKSCAN 2 FFFFFFFF " + str(scanDuration) + "\r\n").encode())
        scanEnd = False
        while not scanEnd:
            line = serial_port.readline()
            if line.startswith(b"EVENT 22"):
                scanEnd = True
            elif line.startswith(b"  "):
                cols_str = line.decode()
                cols = cols_str.strip().split(':')
                scanRes[cols[0]] = cols[1]
        scanDuration+=1
        if 14 < scanDuration and 'Channel' not in scanRes.keys():
            sys.exit()

    # チャネルを設定
    serial_port.write(("SKSREG S2 " + scanRes["Channel"] + "\r\n").encode())
    waitOK(serial_port)

    # PAN IDを設定
    serial_port.write(("SKSREG S3 " + scanRes["Pan ID"] + "\r\n").encode())
    waitOK(serial_port)

    # IPv6アドレスを取得
    serial_port.write(("SKLL64 " + scanRes["Addr"] + "\r\n").encode())
    serial_port.readline()
    ipv6Addr = serial_port.readline().decode().strip()

    # PANA認証
    serial_port.write(("SKJOIN " + ipv6Addr + "\r\n").encode())
    waitOK(serial_port)

    bConnected = False
    while not bConnected:
        line = serial_port.readline()
        if line.startswith(b"EVENT 24"):
            sys.exit(1)
        elif line.startswith(b"EVENT 25"):
            bConnected = True

    return (serial_port, ipv6Addr)

# Main 
if __name__ == '__main__':

    mqtt_client = connect_mqtt()
    mqtt_client.loop_start()
    try: # 例外処理を追加
        (serial_port, ipv6Addr) = initialize()
        serial_port.timeout = 10
        sleep_sec = 9

        while True:
            res_count = 0

            powers = [b'\xEA',b'\xE7',b'\xE8']
            power_iter = itertools.cycle(powers) # powers配列をイテレータに変換
            while True:
                echonetLiteFrame = b'\x10\x81\x00\x01\x05\xFF\x01\x02\x88\x01\x62\x01'
                nextp = next(power_iter) # イテレータから次の要素を取得
                echonetLiteFrame += nextp
                echonetLiteFrame += b'\x00'
                command = ("SKSENDTO 1 {0} 0E1A 1 {1:04X} ".format(ipv6Addr, len(echonetLiteFrame))).encode()
                command += echonetLiteFrame
                command += ("\r\n").encode()
                serial_port.write(command)
                #decode(空)にするとエラーになるので、errors='replace'か errors='ignore'無視を入れる。
                line = serial_port.readline().decode('cp932', errors='replace')

                res_count += 1
                if res_count > 5:
                    break;
                if line.startswith("ERXUDP"):
                    cols = line.split(' ')


                    if len(cols) >= 9:  # colsが9つ以上の要素を持っていることを確認
                        res = cols[8]
                        seoj = res[8:8+6]
                        ESV = res[20:20+2]



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

                            if EPC == "E7": # E7:瞬時電力計測値
                                signedhexPower = line.rstrip()[-8:]
                                intPower = int(signedhexPower, 16)
                                if (intPower >> 31) == 1:
                                    intPower = (intPower ^ 0xFFFFFFFF) * (-1) -1
                                #u = datetime.datetime.utcnow().isoformat("T") + "Z"
                                fields = {'power': intPower}
                                #influx_json(fields, u)
                                influx_json(fields)
                                mqtt_client.publish("SmartMeter/electric/Instantaneous", str(intPower), qos=1, retain=True)
                                #txtファイルに出力
                                with open('/home/go/script/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(u"R:{0}[A]".format(intCurR) + "\n") #1280 153
                                print(u"T:{0}[A]".format(intCurT) + "\n") #54 97 32 61
                                fields = {'E8_R': intCurR , 'E8_T': intCurT }
                                influx_json(fields)
                                mqtt_client.publish("SmartMeter/electric/Instantaneous/R", format(intCurR), qos=1, retain=True)
                                mqtt_client.publish("SmartMeter/electric/Instantaneous/T", format(intCurT), qos=1, retain=True)

                            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)
                                intTimeKW = int(hexTimeKW, 16)#kWh
                                intTimeKW = float(intTimeKW) / 10
                                print(u"teizi{0}[kWh]".format(intTimeKW) + "\n")
                                fields = {'EA': float(intTimeKW) }
                                influx_json(fields)
                                mqtt_client.publish("SmartMeter/electric/Positive", float(intTimeKW), qos=1, retain=True)

                        break;
                time.sleep(sleep_sec)

        serial_port.close()
    except TimeoutError as e: # タイムアウト例外が発生した場合
        print(e)
        sys.exit(0)

systemdに追加

power_mqtt.pyで取得したデータに数値でない文字が入っていて落ちるときがあるので「Restart=on-failure(落ちたら再起動する)」を追加した。(未解決)

sudo vim /etc/systemd/system/smart_meter.service
[Unit]
Description = Smart Meter

[Service]
ExecStart = /usr/bin/python /home/go/script/power_mqtt.py
Type = simple
Restart=on-failure

[Install]
WantedBy = muti-user.target

サービス登録

sudo systemctl daemon-reload
sudo systemctl start smart_meter.service
sudo systemctl enable smart_meter.service
sudo systemctl status smart_meter.service

おかしかったら以下でログを見る。

sudo journalctl -u smart_meter.service

Grafanaの電力復活

InfluxDBもついでに送信するようにしたので、Grafanaの電力も復活した。

ついでにGrafanaでも電気料金を表示するようにする。下のほうで電気料金の表示方法を紹介しています。

Home AssistantのMQTTでデータ受け入れ準備

ここからはHome Assistantのサーバーで操作します。すでに設定 > デバイスとサービスでMQTTをインストールしている状態から説明します。

Home assistantのconfiguration.yaml設定

SmartMeter/electric/Instantaneousなどのstate_topic部分は上のPythonスクリプトのmqtt_client.publishの部分と同じにします。

mqtt:
  sensor:
    - name: "Instantaneous Power"
      state_topic: "SmartMeter/electric/Instantaneous"
      unique_id: instantaneous_power
      device_class: power
      unit_of_measurement: W
      state_class: measurement
      icon: mdi:flash

    - name: "Cumulative Energy Positive"
      state_topic: "SmartMeter/electric/Positive"
      unique_id: cumulative_energy
      device_class: energy
      unit_of_measurement: kWh
      state_class: total_increasing
      icon: mdi:flash

    - name: "Instantaneous Current R"
      state_topic: "SmartMeter/electric/Instantaneous/R"
      unique_id: instantaneous_current_r
      device_class: current
      unit_of_measurement: A
      state_class: measurement
      icon: mdi:flash

    - name: "Instantaneous Current T"
      state_topic: "SmartMeter/electric/Instantaneous/T"
      unique_id: instantaneous_current_t
      device_class: current
      unit_of_measurement: A
      state_class: measurement
      icon: mdi:flash

設定し終わったらHome Assistantを再起動しましょう。

エネルギーとオーバービューに表示

エネルギー > エネルギーの設定(右上点々)で「消費を追加」を押すと以下の表示になる。

定時積算電力量正方向のCumulative Energy Positiveのみ表示されるので選ぶ。

電気料金は段階ごとに違ってくるけど、九州電力の毎月の平均使用KWhが1KWhで25.6円くらいだったので、多めに26円にした。

使用電力量と料金を積分で算出してくれる。

こんな感じ↓で毎時使用電力を算出してくれる。

目標は毎日10KWh以内にしたい。もう少し家族にわかりやすい(節約してくれる)ように、電力量のグラフなど作ってE-Paperに表示させようか。

数日間計測していると、Khadas VIM1S(Home Assistantのサーバー)が調子悪くなって落ちたり電力スマートメーターが調子悪く?通信できなかったりすると↑このようにとんでもない値になってしまって、あまりあてにはできない。

もう少し改善する必要がありそう。

オーバービュー

オーバービューに追加するには以下のようなエンティティを追加していきます。

sensor.instantaneous_current_r
sensor.instantaneous_current_t
sensor.instantaneous_power
sensor.cumulative_energy_positive

instantaneous_current_tは売電時に使うようで、賃貸マンションのウチはほぼゼロのままなので表示しなかった。

Grafanaで消費電力計算

電力スマートメーターUSBドングルのRL7が調子悪くなるのか?電力スマートメーターが調子悪くなるのか?もしくはHome Assistantが調子悪くなるのか、原因がまだわかりませんが、電力を取得できないとHome Assistantがとんでもない値を示すので、Grafanaでも電気料金を計算してグラフにしておきます。

2つのグラフ、一つはEAの値を日毎にして棒グラフにして、もう一つは黄色のラインで電気料金にした。

日毎にグラフを表示させるSQLは以下のようにした。

SELECT difference(distinct("EA")) FROM "power" WHERE $timeFilter GROUP BY time(1d) fill(null)
電気料金が1Kwhあたり約26円だったので以下のようにした。
SELECT difference(distinct("EA"))*26 FROM "power" WHERE $timeFilter GROUP BY time(1d) fill(null)

一日のKwh消費電力は九州電力のページとほぼ一緒ですが、一日ずれているような気がする。

留守の場合でブレーカーを下げない時(3/19~22の間)は料金が安い。

上のクエリーだけではグラフ設定の情報が少ないのでダッシュボードのJSONを貼り付けておきます。

{
  "annotations": {
    "list": [
      {
        "builtIn": 1,
        "datasource": {
          "type": "grafana",
          "uid": "-- Grafana --"
        },
        "enable": true,
        "hide": true,
        "iconColor": "rgba(0, 211, 255, 1)",
        "name": "Annotations & Alerts",
        "type": "dashboard"
      }
    ]
  },
  "editable": true,
  "fiscalYearStartMonth": 0,
  "graphTooltip": 0,
  "links": [],
  "liveNow": false,
  "panels": [
    {
      "datasource": {
        "type": "influxdb",
        "uid": "c54e59f1-387f-4e0a-833b-970ef0f83ce5"
      },
      "fieldConfig": {
        "defaults": {
          "color": {
            "mode": "palette-classic"
          },
          "custom": {
            "axisBorderShow": false,
            "axisCenteredZero": false,
            "axisColorMode": "text",
            "axisLabel": "KWh",
            "axisPlacement": "auto",
            "barAlignment": 0,
            "drawStyle": "bars",
            "fillOpacity": 49,
            "gradientMode": "opacity",
            "hideFrom": {
              "legend": false,
              "tooltip": false,
              "viz": false
            },
            "insertNulls": false,
            "lineInterpolation": "linear",
            "lineWidth": 3,
            "pointSize": 2,
            "scaleDistribution": {
              "type": "linear"
            },
            "showPoints": "auto",
            "spanNulls": false,
            "stacking": {
              "group": "A",
              "mode": "none"
            },
            "thresholdsStyle": {
              "mode": "off"
            }
          },
          "fieldMinMax": false,
          "mappings": [],
          "thresholds": {
            "mode": "absolute",
            "steps": [
              {
                "color": "yellow",
                "value": null
              },
              {
                "color": "red",
                "value": 80
              }
            ]
          },
          "unit": "kwatth"
        },
        "overrides": [
          {
            "matcher": {
              "id": "byName",
              "options": "料金"
            },
            "properties": [
              {
                "id": "custom.drawStyle",
                "value": "line"
              },
              {
                "id": "custom.lineInterpolation",
                "value": "smooth"
              },
              {
                "id": "custom.axisLabel",
                "value": "円"
              },
              {
                "id": "unit",
                "value": "currencyJPY"
              },
              {
                "id": "custom.drawStyle",
                "value": "line"
              }
            ]
          },
          {
            "matcher": {
              "id": "byName",
              "options": "電力量"
            },
            "properties": [
              {
                "id": "custom.lineWidth",
                "value": 8
              }
            ]
          }
        ]
      },
      "gridPos": {
        "h": 13,
        "w": 24,
        "x": 0,
        "y": 0
      },
      "id": 2,
      "options": {
        "legend": {
          "calcs": [],
          "displayMode": "list",
          "placement": "bottom",
          "showLegend": true
        },
        "tooltip": {
          "mode": "single",
          "sort": "none"
        }
      },
      "targets": [
        {
          "alias": "電力量",
          "datasource": {
            "type": "influxdb",
            "uid": "c54e59f1-387f-4e0a-833b-970ef0f83ce5"
          },
          "groupBy": [
            {
              "params": [
                "1d"
              ],
              "type": "time"
            },
            {
              "params": [
                "null"
              ],
              "type": "fill"
            }
          ],
          "hide": false,
          "measurement": "power",
          "orderByTime": "ASC",
          "policy": "default",
          "query": "SELECT difference(distinct(\"EA\")) FROM \"power\" WHERE $timeFilter GROUP BY time(1d) fill(null)",
          "rawQuery": false,
          "refId": "A",
          "resultFormat": "time_series",
          "select": [
            [
              {
                "params": [
                  "EA"
                ],
                "type": "field"
              },
              {
                "params": [],
                "type": "distinct"
              },
              {
                "params": [],
                "type": "difference"
              }
            ]
          ],
          "tags": []
        },
        {
          "alias": "料金",
          "datasource": {
            "type": "influxdb",
            "uid": "c54e59f1-387f-4e0a-833b-970ef0f83ce5"
          },
          "groupBy": [
            {
              "params": [
                "1d"
              ],
              "type": "time"
            },
            {
              "params": [
                "null"
              ],
              "type": "fill"
            }
          ],
          "hide": false,
          "measurement": "power",
          "orderByTime": "ASC",
          "policy": "default",
          "query": "SELECT difference(distinct(\"EA\"))*26 FROM \"power\" WHERE $timeFilter GROUP BY time(1d) fill(null)",
          "rawQuery": true,
          "refId": "B",
          "resultFormat": "time_series",
          "select": [
            [
              {
                "params": [
                  "EA"
                ],
                "type": "field"
              },
              {
                "params": [],
                "type": "distinct"
              },
              {
                "params": [],
                "type": "difference"
              }
            ]
          ],
          "tags": []
        }
      ],
      "title": "電力",
      "type": "timeseries"
    }
  ],
  "refresh": "",
  "schemaVersion": 39,
  "tags": [],
  "templating": {
    "list": []
  },
  "time": {
    "from": "now-7d",
    "to": "now"
  },
  "timepicker": {},
  "timezone": "",
  "title": "New dashboard",
  "version": 0,
  "weekStart": ""
}

上記のJSONをダッシュボードの歯車アイコン > JSON Modelに貼り付ければ表示されるようになります。

ESP32-CAMで水道・ガスメーターのデータを取得

アナログ式なやり方ですが、いまのところ水道やガスメーターの数値を自動で取得する方法が無いので、メーターの数値を画像にして解析する方法でデータを取得していきます。

以下のページにESP32-CAMを使ってデータ化する方法が解説されているのでやってみました。

Installation - AI on the Edge Device

ESP32-CAMをPCと接続

まずはPCと接続してファームウエアをインストールする必要があります。

USB-TTLが必要ですが、MicroUSBボードにBootボタンがあるタイプならば必要なさそう。自分のMicroUSBボードはどれもリセットボタンしかなくUSB-TTLが必要だった。

Io0ボタンがあるMicroUSBボードもありますが、Aliexpressで買ったショップでは画像は上記のようになっていましたが、Io0ボタンが無いのを3つ送ってきたので、文句を言って合計6ドル返してもらった。

仕方がないのでUSB-TTLを買ってPCでファームウェアをインストールする。

USB-TTLESP32-CAM
TXDUOR
RXDUOT
IO0 – GND ショート
5V5V
GNDGND

IO0とGNDは通電させる。上記のように配線して、PCと接続するとシリアルポートから通信できるようになる。

PCからファームウェア インストール

WebブラウザはFirefoxではシリアルポートを認識せずダメなので、EdgeかChromeでアクセスする。

AI on the Edge Device - Webinstaller

上記ページにアクセスしてConnectボタンを押す。

ESP32-CAMが接続されているポートをデバイスマネージャーのポート(COM と LPT)でチェックする。

ESP32-CAMのポートを選んで接続ボタンを押すと以下の用に表示されるので、「INSTALL Al-ON-THE-EDGE」を押してインストールする。

しばらくしてうまくいくと以下のようになる。

これでESP32-CAMをPCから切断してOK。

MicroSDにコピー

続いてMicroSDにもツールをコピーする必要がある。

MicroSDはPCでFATでフォーマットしておく。

自動でインストールする方法もありますが、めんどうそうなのでPCで手動でダウンロードしてMicroSDにファイルを設置します。

Releases · jomjol/AI-on-the-edge-device
Easy to use device for connecting "old" measuring units (water, power, gas, ...) to the digital world - jomjol/AI-on-the...

上記ページのAI-on-the-edge-device__manual-setup__v15.7.0.zipをダウンロードした。

解凍すると↓このような中身になるのでwlan.iniを開いてWifi設定する。

以下のようにssid/passwordでWifiの設定をする。

ローカルのIPアドレスを固定する場合はip/gateway/netmaskを設定する。

wlan.ini/html/config/demo/firmware/img_tmp/logをMicroSDのトップにコピーしましょう。

ESP32-CAMを突っ張り棒で設置

このようにESP32-CAMを設置しました。けっこう邪魔です・・・

左側のガスメーターの手前にある白い突っ張り棒にぶら下がっている黒い物体がESP32-CAM、ケースは3Dプリンターで作った。

ガスメーターは近々、スマートメーターに変更されるということなので(ガススマートメーターはいまのところハックできないようです。)、水道メーターの検針のみ人が見に来る。

水道メーターはESP32-CAMが邪魔だけどなんとか見れるのではないでしょうか?↓邪魔で検針に来た人に怒られるかもしれません・・・

電源は単3電池4つでは?なぜか動かなかった(直列で5.2Vほど出てました)のでモバイルバッテリーが2日ほど持って、モバイルバッテリーを充電する小さいポータブル電源を使って7日ほどもたせるようにした。

(ポータブル電源単体では、ESP32-CAMのような小電力では10分くらいで給電が切れてしまうので使えなかった。)

Webページを表示させて設定する

MicroSDカードをESP32-CAMに刺して起動し、Webブラウザで見ると以下のように表示されたら成功です。

ガスや水道メーターの前あたりにESP32-CAMを設置してNext Stepを押していき設定します。

メーター取得設定

Alignment Markersの設定はメーターの部分?を大きく2箇所を設定しますが、どんな役割なのかよくは分かっていません。適当に数値の周りの2箇所を設定しました。

続いてDigit and Analog ROIsにすすみます。

数値のサイズが違う場合は、Show all ROIs以外のチェックを外して個別に設定するほうがよさそう。

  • Show all ROIs:すべての ROI を表示
  • Lock aspect ratio:アスペクト比をロック
  • Synchronize y, Δx and Δy between ROIs:全てのROIが同じサイズになり、上下移動も同じになる。
  • Keep equidistance of 数値 between all ROIs:全てのROIが指定した数値の等間隔になる。

Config設定

最後にConfigでMQTTとInfluxDB 1系の設定をしました。

設定 > デバイスとサービス > MQTTにある「設定」の「MQTTの再設定」を押す。

上記のポート・ユーザー名・パスワードを以下の設定に入力。

InfluxDBは1系はUserとPassを入力してInfluxDBサーバーに接続する。

これで、MQTTでHomeAssistantサーバーに、InfluxDBサーバーにもデータが送信された。

鮮明でも遠いとデタラメな数値になる

どうしてもLEDライトの光が反射して一部うまく読み取れなかったので、角度を上からにしてちょっと反してみたが、

まったくうまく読み取れなくなりました・・・

1411904が正しい数値で、読み取ったRaw Valueは4051075ととんでもなく違う値になってしまった。やり直し。

ちょっとピンボケしていてもより近くで大きな画像のほうが読み取りやすいみたい。

近づけると↓このように中央の数値に横に光があたってしまい7と識別される。

ESP32-CAMのLEDフラッシュを使わずに別途LEDライトを使う方法もありました。

External LED - AI on the Edge Device

LEDテープライトを買ったので時間がある時にテストしてみます。

水道メーター

ガスメーターと同様にESP32-CAMに突っ張り棒を使ってメーターの前に設置しました。

斜めでも↓Alignment > Reference Image and Camera SettingsのRotation angleで設定して水平になるようにできる。

こんぐらいピンボケでも大きな数値なので読み取りやすいようで、エラーはなかった。

こちらは、1m3単位で最小単位は回転するメモリしかないので、値を取得できない。なので1m3単位でしか計測不可。

2日に1回ほど1m3使ったら表示される。

電気・ガス・水道メーターのデータを取得できたが不完全

ガスメーターと水道メーターはESP32-CAMで画像を撮って数値を取得する方法なので、あまり正確な数値は取得できない場合が多く、信頼性にかける。

ガスメーターはスマートメーター化したのですが、ガス会社のみにデータが送られるようなので、お客はデータを取得する手段がいまのところなさそう。

水道メーターはまったくスマートメーター化する気配を見せない。

電気メーターはスマートメーター化しているので、正確な数値が取得できますが、Python3スクリプトが3日~5日置きに落ちるというかタイムアウトしても復帰しないので、1時間くらいデータが取得できない場合にはsudo systemctl stop・start smart_meter.serviceなどで手動でやり直さないといけないのが今後の課題。

より信頼性のあるデータを取得できるように改善できたら追記していきます。

コメント