2022年 SPRESENSE™ 活用コンテスト 」開催中!ローパワーでハイパフォーマンスなエッジコンピューティングを、あなたの手で活用してみませんか?

eucalyのアイコン画像
eucaly 2022年01月23日作成 (2022年01月23日更新) © MIT
製作品 製作品 Lチカ Lチカ 閲覧数 1240
eucaly 2022年01月23日作成 (2022年01月23日更新) © MIT 製作品 製作品 Lチカ Lチカ 閲覧数 1240

ご家庭消費電力計(デカいの)の作製

ご家庭消費電力計(デカいの)の作製

こんにちは、ゆうかりです。

今回は、以下の記事で中途半端に使ってみたけど放置していたWi-SUN関連機材を使って。
消費電力計を作ってみた、というお話です。

Bルート契約周りとかは、以下記事をご確認くださいな。

obnizでスマートメーターにちょっかいを出してみる
https://elchika.com/article/142b9500-8c1c-4b7d-8d38-b515c752925b/

消費電力の可視化

まあ、プロダクトベースだとタブレットだの太陽電池コントローラ上に表示する系の「HEMS」機器や。
キャプションを入力できます
https://sumai.panasonic.jp/aiseg/hems/index.html
m5stack系オプションに、m5stickCな画面に表示するステキさんとかがあります。
キャプションを入力できます
https://booth.pm/ja/items/1650727

・・・まあこのヘン使うのが、気軽でいいですよ、と。

このお話は、そのへんぶっちぎって適当にWi-SUNモジュールをobniz案件だ!っつーて買った挙句に放置してしまって勿体ないので使いましょう、という、かなりこう意識が低いお話です。

買って放置してたWi-SUNモジュール、ROHMのBP35C0という、まあ比較的使い辛いやーつ。
キャプションを入力できます

ラズパイでとりあえず制御できるか

ラズパイと、秋月のFT232基板使って、適当に組みました。
通信はUSB変換してUSB経由にする方向で。
キャプションを入力できます

注意点としては、FT232はIOをスイッチで3.3Vにすることが出来ますが、3.3V電源は搭載していない為、別途用意する必要があること、くらいです。
適当に組んでる時は、ラズパイから3.3Vを貰ってましたとさ。

で、動作確認。
スクリプトは以下URLなどを参照に。
https://qiita.com/rukihena/items/82266ed3a43e4b652adb

https://github.com/katsumin/python-echonet-lite

なんだけど、、、最近のRaspberry Pi OSはpythonが3系しか搭載しておらず。
まあ結構大胆にスクリプト書き替えが必要でしたとさ、メンディー・・・。
キャプションを入力できます

基板作製

ま、動作確認はうまくいったので。
サクッと基板を作りましょう。
キャプションを入力できます
秋月のUSBシリアル変換、東芝の3.3V LDO、そして秋月C基板です。

適当に配置検討して。
キャプションを入力できます

配線!。
キャプションを入力できます

ま、配線は以下を参考に。
キャプションを入力できます

こんなかんじで。
RESET端子、制御必要かと思いきや、単純にVCCに吊っちゃっていいみたいです。

CN No 端子名 接続
CN1 1 GND GND
CN1 2 ADC1 未接続
CN1 3 ADC2 未接続
CN1 4 VCC 3.3V
CN1 5 VCC 3.3V
CN1 6 GPIOA7 未接続
CN1 7 MODE2 GND
CN1 8 MODE0 GND
CN1 9 GND GND
CN2 1 GND GND
CN2 2 RTS 未接続
CN2 3 CTS 未接続
CN2 4 RXD FT232 TX
CN2 5 TXD FT232 RX
CN2 6 SCL 未接続
CN2 7 RESET 3.3V
CN2 8 SDA 未接続
CN2 9 GND GND

組み立て

ま、適当なマトリクスLED板と、接続基板を用意して。
キャプションを入力できます

できました。
キャプションを入力できます

スクリプト

以下2つを作製。
いずれも、systemdでの自動起動対応、自動リブート用にリセット処理を入れている感じです。

まず、消費電力をメーターから読み取って、「/ramdisk/watt.data」に書き出すスクリプト。

getwatt.py

#!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys import serial import time import os # Bルート認証ID rbid = 'BルートのID' # Bルート認証パスワード rbpwd = 'Bルートのパスワード' # シリアルポートデバイス名 serialPortDev = '/dev/ttyUSB0' # リザルトファイル outputfilename = '/ramdisk/watt.data' def debugwrite(data): # print (data) pass def sendcommand(serialobject,data): data = data + '\r\n' serialobject.write(data.encode()) readdata = serialobject.readline() readdata = serialobject.readline() debugwrite (readdata.decode()) def sendcommandraw(serialobject,data): data = data + '\r\n' serialobject.write(data.encode()) def sendcommandbinary(serialobject,data): serialobject.write(data) # serialobject.flush() def receivecommandraw(serialobject): readdata = serialobject.readline() return (readdata) # main loop while True: # シリアルポート初期化 if not (os.path.exists(serialPortDev)): time.sleep(10) continue debugwrite('[init]') ser = serial.Serial(serialPortDev, 115200) # とりあえずバージョンを取得してみる(やらなくてもおk) debugwrite('[version]') sendcommand(ser,'SKVER') # Bルート認証パスワード設定 debugwrite('[password]') sendcommand(ser,'SKSETPWD C ' + rbpwd) # Bルート認証ID設定 debugwrite('[id]') sendcommand(ser,'SKSETRBID ' + rbid) scanDuration = 4; # スキャン時間。サンプルでは6なんだけど、4でも行けるので。(ダメなら増やして再試行) scanRes = {} # スキャン結果の入れ物 debugwrite('[scan]') # スキャンのリトライループ(何か見つかるまで) while not 'Channel' in scanRes: # アクティブスキャン(IE あり)を行う # 時間かかります。10秒ぐらい? sendcommandraw(ser,'SKSCAN 2 FFFFFFFF ' + str(scanDuration) + ' 0') # スキャン1回について、スキャン終了までのループ scanEnd = False while not scanEnd : line = receivecommandraw(ser) debugwrite (line) if line.startswith(b'EVENT 22') : # スキャン終わったよ(見つかったかどうかは関係なく) scanEnd = True elif line.startswith(b' ') : # スキャンして見つかったらスペース2個あけてデータがやってくる # 例 # Channel:39 # Channel Page:09 # Pan ID:FFFF # Addr:FFFFFFFFFFFFFFFF # LQI:A7 # PairID:FFFFFFFF linedecode =line.decode() cols = linedecode.strip().split(':') scanRes[cols[0]] = cols[1] scanDuration+=1 if 10 < scanDuration and not 'Channel' in scanRes: # 引数としては14まで指定できるが、7で失敗したらそれ以上は無駄っぽい continue if scanEnd == False: ser.close() time.sleep(10) continue # スキャン結果からChannelを設定。 debugwrite('[set channnel]') sendcommand(ser,'SKSREG S2 ' + scanRes['Channel']) # スキャン結果からPan IDを設定 debugwrite('[set panid]') sendcommand(ser,'SKSREG S3 ' + scanRes['Pan ID']) # MACアドレス(64bit)をIPV6リンクローカルアドレスに変換。 # (BP35A1の機能を使って変換しているけど、単に文字列変換すればいいのではという話も??) debugwrite('[set localaddress]') sendcommandraw(ser,'SKLL64 ' + scanRes['Addr']) readdata = receivecommandraw(ser) ipv6Addr = receivecommandraw(ser).decode().strip() # print(ipv6Addr) # PANA 接続シーケンスを開始します。 debugwrite('[join]') sendcommand(ser,'SKJOIN ' + ipv6Addr) # PANA 接続完了待ち(10行ぐらいなんか返してくる) debugwrite('[wait panaconnect]') bConnected = False bCount = 0 while not bConnected : line = receivecommandraw(ser) debugwrite(line) if line.startswith(b'EVENT 24') : continue elif line.startswith(b'EVENT 25') : # 接続完了! bConnected = True bCount = bCount + 1 if bCount > 30: continue if bConnected == False: ser.close() time.sleep(10) continue # これ以降、シリアル通信のタイムアウトを設定 ser.timeout = 2 # スマートメーターがインスタンスリスト通知を投げてくる # (ECHONET-Lite_Ver.1.12_02.pdf p.4-16) readdata = receivecommandraw(ser) # 無視 readdata = receivecommandraw(ser) # 無視 time.sleep(2) debugwrite('[start]') protime = time.time() while True: ntime = time.time() ptime = ntime - protime if ptime > 120: break # ECHONET Lite フレーム作成 #  参考資料 #  ・ECHONET-Lite_Ver.1.12_02.pdf (以下 EL) #  ・Appendix_H.pdf (以下 AppH) echonetLiteFrame = b'' echonetLiteFrame += b'\x10\x81' # EHD (参考:EL p.3-2) echonetLiteFrame += b'\x00\x01' # TID (参考:EL p.3-3) # ここから EDATA echonetLiteFrame += b'\x05\xFF\x01' # SEOJ (参考:EL p.3-3 AppH p.3-408~) echonetLiteFrame += b'\x02\x88\x01' # DEOJ (参考:EL p.3-3 AppH p.3-274~) echonetLiteFrame += b'\x62' # ESV(62:プロパティ値読み出し要求) (参考:EL p.3-5) echonetLiteFrame += b'\x01' # OPC(1個)(参考:EL p.3-7) echonetLiteFrame += b'\xE7' # EPC(参考:EL p.3-7 AppH p.3-275) echonetLiteFrame += b'\x00' # PDC(参考:EL p.3-9) # コマンド送信 command = 'SKSENDTO 1 {0} 0E1A 1 0 {1:04X} '.format(ipv6Addr, len(echonetLiteFrame)) command = command.encode() + echonetLiteFrame + b'\r\n' sendcommandbinary(ser,command) for loopi in range(10): line = receivecommandraw(ser) # 受信データはたまに違うデータが来たり、 # 取りこぼしたりして変なデータを拾うことがあるので # チェックを厳しめにしてます。 if line.startswith(b'ERXUDP') : cols = line.strip().split(b' ') res = cols[9] # UDP受信データ部分 seoj = res[4:7] ESV = res[10:11] if seoj == b'\x02\x88\x01' and ESV == b'\x72' : # スマートメーター(028801)から来た応答(72)なら EPC = res[12:13] if EPC == b'\xe7' : # 内容が瞬時電力計測値(E7)だったら hexPower = res[14:18] # 最後の4バイト(16進数で8文字)が瞬時電力計測値 intPower = int.from_bytes(hexPower, 'big') strPower = str(intPower) if len(strPower) > 0: f = open(outputfilename,'w') f.write (str(intPower)) f.close() protime = time.time() time.sleep(5) break ser.close()

rpi-rgb-led-matrixライブラリを使って、「/ramdisk/watt.data」の内容を表示するスクリプト。

ledoutput.py

from PIL import Image, ImageDraw, ImageOps, ImageFont, ImageFilter, ImageChops, ImageColor from rgbmatrix import RGBMatrix, RGBMatrixOptions import time outputfilename = '/ramdisk/watt.data' class ledworker(object): def ledinit(self): self.options = RGBMatrixOptions() self.options.hardware_mapping = "regular" self.options.led_rgb_sequence = "RGB" self.options.rows = 32 self.options.chain_length = 2 self.options.parallel = 1 self.options.pwm_bits = 11 self.options.brightness = 70 self.options.pwm_lsb_nanoseconds = 130 self.options.gpio_slowdown = 3 self.matrix = RGBMatrix(options = self.options) self.fontdata = ImageFont.truetype('/data/Envy Code R Bold.ttf', 32) self.fontdatasmall = ImageFont.truetype('/data/Envy Code R Bold.ttf', 12) def ledoutput(self,data): image = Image.new('RGB', (64, 32), (0, 0, 0)) draw = ImageDraw.Draw(image) outputdata = float(data) / 1000 outputdata = '{:.04f}'.format(outputdata) draw.text((8,-5), '.', (192, 192, 192),font=self.fontdata) draw.text((52,20), 'kw', (192, 192, 192),font=self.fontdatasmall) draw.text((0,-5), outputdata[0], (255, 255, 255),font=self.fontdata) draw.text((17,-5), outputdata[2], (255, 255, 255),font=self.fontdata) draw.text((33,-5), outputdata[3], (255, 255, 255),font=self.fontdata) draw.text((49,-5), outputdata[4], (255, 255, 255),font=self.fontdata) self.matrix.SetImage(image) led = ledworker() led.ledinit() while True: try: f = open(outputfilename, 'r') data = f.read() f.close() led.ledoutput(data) time.sleep(1) except: pass

フォントは、「Envy Code」を使っています。
https://damieng.com/blog/2008/05/26/envy-code-r-preview-7-coding-font-released/

アンテナの選定

さて、今回使用しているWi-SUNモジュールには。
アンテナが付属しておりません。
で、Wi-SUNの物理層には、「LQI(Link Quality Indication)」という、リンク品質を確認する項目が定義されており、まあそれを使ってアンテナの簡易的な評価をすることができます。
手持ちのアンテナや、そのへんに売っているアンテナを使って、比較してみました。

比較ラインアップは、以下の通り。

適当な2.4GHz用アンテナ

NUC的なパソコンについてきたやーつ。
多分WiFiだのBTだの用。
キャプションを入力できます

適当なリグ用の標準アンテナ

八重洲のVX-8だそうです。
キャプションを入力できます

900MHz帯受信にも対応しているホイップアンテナ

ダイヤモンドアンテナの、SRHF10、アキバの富士無線さんで購入。
キャプションを入力できます

秋月のラバーダックアンテナ

広帯域で、お安い!。
キャプションを入力できます

アンテナは、秋月最強でした

ほぼ同一条件で、アンテナだけ変更して。
ま、サクッと測ってみたら。

アンテナ LQI
2.4GHzアンテナ 28
VX-8アンテナ 9C
SRHF10 60
秋月アンテナ B1

うむ、高くないし、秋月アンテナ使いましょう!。
 *アンテナ感度とか確認してないので、よいこはまねしないでくださいね・・・。

というわけで、出来ました!

おおきいことはいいことだ!。
キャプションを入力できます

いやあ、消費電力がバーンと出てるので、エアコンつけっぱなしとか一発で分かっていい感じです!。
ま、消費電力自体はファイルに書き出しているので、FirebaseだのZabbixだの連携も組もうと思えば組めますが。
とりあえず「その場でパッと分かって節電」的な用途は、満たせた感じ!。
分かりやすいって大事よね!。

ま、つーわけで。
なかなかに満足のいけるものができましたとさ!。

以上です。

2
eucalyのアイコン画像
いつも、てきとうです
  • eucaly さんが 2022/01/23 に 編集 をしました。 (メッセージ: 初版)
  • eucaly さんが 2022/01/23 に 編集 をしました。
  • Opening
    ozwkのアイコン画像 ozwk 2022/01/24

    技適はモジュールとアンテナの組み合わせで取得するものだった気がするのですが、本例は大丈夫でしょうか?
    BP35C0のハードウェア仕様書を見ると、

    18 無線設備としての注意事項
    3) 専用外付けアンテナをご使用の場合は、別途お問い合わせください。

    とありますし、

    BP35C0 日本電波法認証済外付けアンテナリスト

    という資料も存在しています。

    0 件の返信が折りたたまれています
  • Opening
    eucalyのアイコン画像 eucaly 2022/01/25

    技適取得条件につきましては、確認しておりません。
    アンテナ評価につきましては、当該資料を確認の上、
    ・無指向性
    ・利得3dbi以下の構造
    のもので、アンテナマッチングを無視して行っているので、利得要件には引っかかってはおりません。

    0 件の返信が折りたたまれています
ログインしてコメントを投稿する