2016年6月13日月曜日

太陽光パネルの発電量をraspberry piでサーバーに送信する方法


太陽光パネルを設置した時からやりたかったことが実現できたので、共有します。

使ったもの

■Tracerのチャージコントローラを使った太陽光発電システム

Tracer 2210RNと100wのパネル4枚を組み合わせたシステムを利用しました。



発電システムの詳細はこちら。

21万円で100Wの太陽光パネル4枚を使った24v230Ah(5420Wh)の蓄電システムを組む

■インターネットに繋がったraspberry pi


今回は4Gドングルでインターネットに接続したraspberry pi B+を使いました。
wifiやLANケーブルでのインターネット接続でも問題ありません。

今回使用したドングルの設定方法詳細はこちら。

Raspberry piでPIXELAのLTE対応USBドングルを使う方法

■チャージコントローラとRaspberry piを接続する道具

  • LANケーブル
  • LANケーブルDIP化キット
  • ユニバーサル基板
  • オスメスジャンパワイヤ


■インターネットでログが確認できるサービス


ThingSpeekは、汎用的に使えそうなデータ収集サービスです。

物理的な接続方法

このように接続します。


実際に接続すると、このようになりました。


フランス語のサイトですが、情報が整理されていたので、通信内容の参考になりました。
Contrôle de chargeur solaire

solar chargerについて議論しているraspberry piのフォーラムです。
Interfacing with Solar Charge Controller

api_keyの取得

ThingSpeekでチャンネルを作成し、write api keyを取得してください。

https://thingspeak.com/

今回はこのようなチャンネル設定にしました。


api_keyはここにあります。


プログラムの設定

raspberry piの適当な場所に下記のプログラムを作成します。
mkdir ~/autorun_programs/
vi ~/autorun_programs/solar_power_logger.py

httpリクエスト以外のprintはコメントアウトしていますが、内容を確認したい場合は「#」を取って動作させてみてください。

~/autorun_programs/solar_power_logger.py
import serial
import time
import requests

def hex_2byte_to_int(hex_2byte):
  return int(hex_2byte[1].encode('hex') + hex_2byte[0].encode('hex'), 16)

tracer_start_command = "\xAA\x55\xAA\x55\xAA\x55\xEB\x90\xEB\x90\xEB\x90"
tracer_id = "\x16"
tracer_kind_command = "\xA0\x00\xB1\xA7\x7F"

tracer_command_to_send = tracer_start_command + tracer_id + tracer_kind_command

port = serial.Serial("/dev/ttyAMA0", baudrate=9600, timeout=1.0)
port.open()
time.sleep(1)
port.flushInput()
port.flushOutput()

while (1):
  panel_volt_sum            = 0
  battery_charge_ampere_sum = 0
  battery_volt_sum          = 0

  # get average of 6 time in 1 min
  for i_per_10sec in range(6):
    port.write(tracer_command_to_send)
    #print 'send: ' + tracer_command_to_send.encode('hex')

    #port.flush()
    data = port.read(100)
    #print 'received: ' + data.encode('hex')
    #print ''

    #print 'syncro: '  + data[0:6].encode('hex')
    #print 'address: ' + data[6].encode('hex')
    #print 'command: ' + data[7].encode('hex')
    length = int(data[8].encode('hex'), 16)
    #print 'length: '  + data[8].encode('hex') + '(' + str(length) + ')'
    info = data[9:9+length]
    #print 'info: '  + info.encode('hex')
    #print 'crc16: ' + data[9+length:11+length].encode('hex')
    #print 'end: '   + data[11+length].encode('hex')

    #print ''
    battery_volt = hex_2byte_to_int(info[0:2])
    #print 'battery_volt: ' + str(battery_volt)
    panel_volt = hex_2byte_to_int(info[2:4])
    #print 'panel_volt: ' + str(panel_volt)
    #print 'reserved: ' + str(hex_2byte_to_int(info[4:6]))
    #out_ampere = hex_2byte_to_int(info[6:8])
    #print 'out_ampere: ' + str(out_ampere)
    #out_volt = hex_2byte_to_int(info[8:10])
    #print 'out_volt: ' + str(out_volt)
    #batt_full_volt = hex_2byte_to_int(info[10:12])
    #print 'batt_full_volt: ' + str(batt_full_volt)
    #print 'outputting?: ' + info[12].encode('hex')
    #print 'over_output?: ' + info[13].encode('hex')
    #print 'loading_to_circuit?: ' + info[14].encode('hex')
    #print 'reserved: ' + info[15].encode('hex')
    #print 'is_overloaded_battery?: ' + info[16].encode('hex')
    #print 'is_overdischarged_battery?: ' + info[17].encode('hex')
    #print 'is_full?: ' + info[18].encode('hex')
    is_charging = int(info[19].encode('hex'), 16)
    #print 'is_charging?: ' + str(is_charging)
    #print 'battery_temp: ' + str(int(info[20].encode('hex'), 16) -30)
    battery_charge_ampere = hex_2byte_to_int(info[21:23])
    #print 'charge_ampere: ' + str(battery_charge_ampere)
    #print 'reserved: ' + info[23].encode('hex')

    #print is_charging
    #print float(battery_volt) / 100
    #print float(panel_volt) / 100
    #print float(charge_ampere) / 100
    #print ''
    panel_volt_sum            += float(panel_volt)            / 100
    battery_charge_ampere_sum += float(battery_charge_ampere) / 100
    battery_volt_sum          += float(battery_volt)          / 100
    time.sleep(10)

  panel_volt_to_send            = panel_volt_sum            / 6
  battery_charge_ampere_to_send = battery_charge_ampere_sum / 6
  battery_volt_to_send          = battery_volt_sum          / 6

  # for thingspeak
  r = requests.post('https://api.thingspeak.com/update.json',
    data = {
      'field1' : panel_volt_to_send,
      'field2' : battery_charge_ampere_to_send,
      'field3' : battery_volt_to_send,
      'field4' : battery_volt_to_send * battery_charge_ampere_to_send,
      'api_key' : '[your api_key]',
    })
  print r.text

プログラム作成にあたっては、「物理的な接続方法」で紹介したリンクと、下記のプログラムを参考にしました。
Arduino向けに書かれたチャージコントローラとの通信プログラムです。
tracer/arduino/Tracer/Tracer.ino

実行

下記のコマンドで実行します。
python ~/autorun_programs/solar_power_logger.py

自動起動にしたい場合は、crontabにreboot後の処理として上記のコマンドを記述します。
crontab -e
@reboot python ~/autorun_programs/solar_power_logger.py &

自動起動のもうひとつの手段としてrc.localがありますが、今回の場合この手段は使えません。
なぜなら、gpioが有効になるのはrc.localが実行された後だからです。
GPIO state after boot

データ確認

thingspeekにデータが登録されていることを確認します。

自分が登録しているデータは、下記のリンクで確認できます。

https://thingspeak.com/channels/124019

iframeで埋め込むことも可能です。
<iframe
  width="450"
  height="250"
  src="https://api.thingspeak.com/channels/124019/charts/4"
></iframe>


iframeのurlにパラメータを指定することで、グラフの見せ方を変えられます。
詳しくはこちらのサイトをご参考ください。
Create a Chart

<iframe
  width="100%"
  height="250"
  src="https://api.thingspeak.com/channels/124019/charts/4?average=60&width=auto&yaxismin=0&color=%236fd16f&results=1440"
></iframe>
以上です。
面白がってもらえたり、何かの参考になれば嬉しいです。

その他参考

下記のサイトの説明で、通信で取得できる電流値はバッテリーへの電流だと分かりました。
EPsolar製 コントローラーオプション

0 件のコメント :