Raspberry Piで測った消費電力をAdafruit IOでグラフ化する -前編-

Raspberry Piで測った消費電力をAdafruit IOでグラフ化する -前編-

数年前、Raspberry Piと電流センサを組み合わせ、Webサービスにアップロードする消費電力計を作成しました。

http://dolls.tokyo/raspberry-pi%e3%81%a8iot%e5%90%91%e3%81%91%e3%82%b0%e3%83%a9%e3%83%95%e3%82%b5%e3%83%bc%e3%83%93%e3%82%b9%e3%80%8cm2x%e3%80%8d%e3%81%a7%e5%ae%b6%e3%81%ae%e6%b6%88%e8%b2%bb%e9%9b%bb%e5%8a%9b%e3%82%92

このときに使っていたデータ・ストレージ&プロットサービスが”M2X”。
これが有料化されてしまい、我が家の電力監視はストップしたままでした。

代替えが中々見つからなかったのですが、今回Adafruit IOに引っ越せましたので手順を残します。

Adafruit IO ?

最初にデータの受け口になってくれるAdafruit IOについて。

提供元のAdafruit(エイダフルーツと読む)はRaspberry Pi & Arduino周りパーツで有名なアメリカのベンダー。
電子工作に関する面白い事を色々公開しているギークな会社。

彼らはデータの良いアウトプット先が見つからなかったため、自分たちでWebサービスを作ったそうです。
それがAdafruit IO。

Adafruit IOはAPIやMQTTによるデータを受け取るストレージ、スマホやブラウザで使うUIを自由にレイアウトできるDashboard、他サービスへのトリガーなどのサービスを提供してくれます。

プランには無料の”IO Free”と、有料の”IO+”があります。
”IO Free”のスペックはこんな感じ。

  • 30 data points per minute
  • 30 days of data storage
  • Triggers every 15 minutes
  • 10 feeds
  • 5 dashboards

IFTTTとの連携も可能らしく、PからVへの橋渡しをうまくしてくれそうな期待感があります。

気をつけなければならないのはデータの保存期間。
30日間しかデータを保存してくれないため、長期のデータは自前で保管しておく必要があります。
1分あたり30個のデータを受け付けてくれるのは、M2Xの2.2個/分からすると大分自由ですね。

有料の”IO+”は60日間のデータ保管、60個/分のデータ受付、天気サービス連携などがついて$10/月または$99/年

ラズパイ1+最新RaspiOSを準備

5年前のRaspbianでは色々不都合が出たため、最新のRaspberry Pi OSで1からセットアップしました。

Raspberry Pi OS Lite
Release date: August 20th 2020
Kernel version: 5.4
Size: 438MB

Wi-Fiの有効化

Raspberry Pi 1には無線がついていません。
前回通り、ジャンクで買ったPLANEX GW-USMicroNで無線化します。

(2020/11/更新版) noobsから導入したraspbianでPLANEX GW-USMicroNを使うメモ

最新OSでは、Wi-Fiドングルは最初から認識しました。
手順は上記記事を更新しましたので、そちらを御覧ください。

再起動後も自動的に繋げたいなら以下が必要。

/etc/wpa_supplicant/wpa_supplicant.confを作成した後は、/etc/rc.localに自動接続のためのコマンド
 ”wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf
を追記して再起動

「一度wpa_supplicantで設定すれば自動的に繋がる。」
みたいな記事が多かったけれど、自分が試した限りではそんな事なかった。
OSイメージによって差があるのかもしれませんが。

rubyenvからrubyをインストール

LiteバージョンはRubyが入っていません。
今は”rubyenv”を使って入れるのが流儀?らしいので従います。

Raspberry piにRubyの最新版をインストールする – Qiita

rbenvのインストールして設定をbashの設定ファイルに書き込む, zshなど使う場合はそれぞれに合わせること

> sudo apt-get install rbenv

> echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> .bashrc
> echo 'eval "$(rbenv init -)"' >> .bashrc

> sudo apt-get install git
> git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
> rbenv install --list

シェルの環境設定ファイルを作成し、rubyのビルドリストを取得・更新しています。

大量のリストの中から、2.7.2をインストール。

インストール済みバージョンの確認
# ruby -v
ruby 2.5.5p157 (2019-03-15 revision 67260) [arm-linux-gnueabihf]

2.7.2のインストール
# rbenv install 2.7.2
Downloading ruby-2.7.2.tar.bz2... -> https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.2.tar.bz2 Installing ruby-2.7.2... Installed ruby-2.7.2 to /root/.rbenv/versions/2.7.2

rbenvでの利用バージョン確認(*がついている方)
# rbenv versions
* system (set by /root/.rbenv/version)
2.7.2

切り替え
# rbenv global 2.7.2

再確認
# rbenv versions
system
* 2.7.2 (set by /root/.ruby-version)
# ruby -v 
ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [armv6l-linux-eabihf] 

rubyenvはダウンロードしたものをユーザーホーム配下に格納します。
環境はユーザーごとに作る必要があるため、今回はrootに作りました。

GPIOライブラリのインストール

最後にRuby用のGPIOライブラリ、piperをインストール。

# gem install pi_piper
Fetching ffi-1.13.1.gem
Fetching pi_piper-2.0.0.gem
Fetching eventmachine-1.0.9.gem
Building native extensions. This could take a while...
Successfully installed eventmachine-1.0.9
Building native extensions. This could take a while...
Successfully installed ffi-1.13.1
Successfully installed pi_piper-2.0.0
Parsing documentation for eventmachine-1.0.9
Installing ri documentation for eventmachine-1.0.9
Parsing documentation for ffi-1.13.1
Installing ri documentation for ffi-1.13.1
Parsing documentation for pi_piper-2.0.0
Installing ri documentation for pi_piper-2.0.0
Done installing documentation for eventmachine, ffi, pi_piper after 406 seconds
3 gems installed

過去の記事のコードをそのまま実行し、特に変更なく動く事を確認できました。

Adafruit io ruby client

次に、結果をAdafruit ioへ書き込む部分を作っていきます。

Adafruit ioは国内の実例を見つけられませんでした。
公式ガイドを意訳しながら試行錯誤したので、間違いは笑ってやって下さい。

ioに書き込むライブラリ

まずシンプルにadafruit ioにデータをアップできるか確認します。
今回は公開されているRubyクライアントを使用しました。

GitHubには詳しいHowtoも書かれています。

まずはモジュールをインストール。

# gem install adafruit-io
Fetching: multipart-post-2.1.1.gem (100%)
Successfully installed multipart-post-2.1.1
Fetching: faraday-0.17.3.gem (100%)
Successfully installed faraday-0.17.3
Fetching: faraday_middleware-0.14.0.gem (100%)
Successfully installed faraday_middleware-0.14.0
Fetching: thread_safe-0.3.6.gem (100%)
Successfully installed thread_safe-0.3.6
Fetching: minitest-5.14.2.gem (100%)
ERROR: Error installing adafruit-io:
minitest requires Ruby version < 3.1, >= 2.2.

次に下記コードでテストします。
3-4行目のusername/api_keyは、サイトのメニューバーにある”My Key”を押すと表示されるUsername/Active Keyに置き換えてください。

require 'adafruit/io'

username = '自分のusername'
api_key = 'aio_XXXXXXXXXXXXXXXXXXXXXXXX'


api = Adafruit::IO::Client.new key: api_key, username: username

# create a feed
puts "create"
garbage = api.create_feed(name: "test-watt-power")

# add data
puts "add data"
api.send_data garbage, 'test_data_1 40W'
api.send_data garbage, 'test_data_2 220W'

end

うまく動けば、”Feeds”にあるDefaultフィードグループへ、”test-watt-power”というFeedが作成されます。

またFeedの中には、2個のテストデータが書き込まれます。

MQTTでPublishする2つの方法

APIを使った方法では、一度書き込んだ後は追記できない仕様の様です。
これでは定期的に測ったデータをアップする事ができません。

そこで定期更新に向く、MQTTを使ったフィードの書き込み方法を試しました。

2つのデータを毎回書き込む、シンプルなコードでテストしました。

require 'adafruit/io'

username = 'ユーザー名'
key = 'アクティブキー'
feedA = 'フィードの名前A'
feedB = 'フィードの名前B'
testnumberA = 'テストデータA'
testnumberB = 'テストデータB'

connection_opts = {
  port: 8883,
  uri: 'io.adafruit.com',
  protocol: 'mqtts'
}

mqtt = Adafruit::IO::MQTT.new username, key, connection_opts

mqtt.publish feedA, testnumberA
mqtt.publish feedB, testnumberB

end

この結果は、上記APIでの書き込みと似たデータになります。
実行するたびにデータを増やして行く事ができます。

mqtt.publishメソッドでは、無条件にDefaultフィードグループへフィードが作成され、値が書き込まれます。
これにフィードグループを指定したい場合、mqtt.publish_groupメソッドを使います。

mqtt.publish feedA, testnumberA
mqtt.publish feedB, testnumberB

↓

mqtt.publish_group('フィードグループの名前', {
  'feedA' => testnumberA,
  'feedB' => testnumberB,
})

うまく希望するフィードグループへフィードへデータを書き込む事ができました。

完成した消費電力計コード

書き込み手段が確定できましたので、これを過去のコードへマージします。

require "pi_piper"
require 'adafruit/io'

#adafruit io account
username = 'ユーザー名'
api_key = 'アクティブキー'
fd_key0 = "watt-power_ch0"
fd_key1 = "watt-power_ch1"
connection_opts = {
  port: 8883,
  uri: 'io.adafruit.com',
  protocol: 'mqtts'
}


#Power Factor
pf = 1.1

#Vref:MCP3208 Refarence voltage ( pin 13)
v_ref = 3.3

#Register
rl = 1000

#Coupling constant
kt = 0.98

#coil turn
turn = 3000

#Sampling time ( Number / sleep time )
sampling = 20

#Sensing
pow0 = 0
pow1 = 0
read_ch = 0
count = sampling * 2
RESOLUTION_AT_12BIT = 0b111111111111.to_f.freeze

mqtt = Adafruit::IO::MQTT.new username, api_key, connection_opts

PiPiper::Spi.begin do |spi|

while count > 0 do
    case read_ch
     when 0 then _, center, last = spi.write [0b00000110, 0b00000000, 0b00000000]
     when 1 then _, center, last = spi.write [0b00000110, 0b01000000, 0b00000000]
    end
    center = center & 0b00001111
    center = center << 8
    value =  center + last
    eo    =  ( ( value / RESOLUTION_AT_12BIT ) * v_ref ).round(4)
    io    =  ( eo / ( rl.to_f / turn.to_f * 0.9.to_f * kt ) ).round(4)
    pow   =  ( 100 * io * pf ).round(4)
    case read_ch
     when 0 then (
      pow0 = pow0 + pow
      read_ch = 1
     )
     when 1 then (
      pow1 = pow1 + pow
      read_ch = 0
     )
    end
    count = count - 1
    sleep 0.25
end

#output
pow0 = ( pow0 / sampling ).round(1)
pow1 = ( pow1 / sampling ).round(1)

puts "[ch0 = %sW, ch1 = %sW]" % [pow0, pow1]

mqtt.publish_group('Epower', {
    fd_key0 => pow0,
    fd_key1 => pow1,
})

puts "[send data %s]" % [Time.now]

end

#sensingのあたりの内訳を知りたい方は、過去記事を御覧ください。

Raspberry Piとクランプセンサで家の消費電力を測ろう(測定部)

実行結果です。

[ch0 = 282.4W, ch1 = 81.2W]
[send data 2020-12-09 00:10:23 +0900]

機能は

  • コンソールに測定データを表示します。
  • “Epower” feed-groupに、”watt-power_ch0″と”…1″という2つのfeedを作成します
  • 作成した2つのフィードにそれぞれch0,ch1の計測値が入ります。
  • 実行するたびに値を追記します。

なお、ライブラリ製作者様は「コードの中に資格情報を入れるな(Whenever possible, we recommend you keep your Adafruit IO API credentials out of your application code by using environment variables. All the examples)」と書いていますがそんな余裕は無し。

RubyをCronで定期実行

コードのテストが完了したので、crontabで5分おきに実行するよう設定します。

例によって、シェルでのコマンドをそのまま書いても動きません。

crontabでrubyを動かすには、パスを個別に通してやる必要があるそうです。
いくつかの手法の中から、シェルスクリプトを用意しなくていいこの方法を採用させていただきました。

cronでrbenvのrubyを使う3つの方法 – sonots:blog

やり方3: bash -l を使う
おそらくこれが一番シンプル。-l オプションを使って .bash_profile を読み込んで実行する。

* * * * * sonots /bin/bash -lc ‘cd /path/to/program-dir && ruby program.rb’
端末で実行する状況とcron で実行される状況が同じになるのでデバグも捗る。

もちろん ~/.bash_profile に以下が書いてある事が前提。

export PATH=”$HOME/.rbenv/bin:$PATH”
eval “$(rbenv init -)”

./bash_profileはインストール時に作ってあるので問題ありません。
crontabにはこの様に書きました。ログも採取する設定です。

# crontab -l

*/5 * * * * /bin/bash -lc 'cd /usr/local/etc/piper && ruby epower.rb > /var/log/cronlog_epower.log 2>&1'

次回へつづく

思った以上に長くなったため、残りは次回。

後半は作成したフィードをグラフにし、一覧できるDashboardを作成します。
また、完成までに起きた様々なトラブルを公開します。

2020/12/12 後編を公開しました

Raspberry Piで測った消費電力をAdafruit IOでグラフ化する -後編-

コメントを残す