日記とか、工作記録とか

自分に書けることを何でも書いてゆきます。作った物、買ったもの、コンピュータ系の話題が多くなるかもしれません。

ラズパイでセンサーのデータを継続的に記録する(ソフトウェア編)

前回の続きで、Raspberry pi 2に接続した温度センサーから繰り返しデータを取得して、自室の気温の変化を見てみたいと思います。SPIデバイスの準備から、Pythonモジュールの準備など、いろいろ大変でした。忘れてしまってここに書き切れていない部分があるかもしれません。以下順を追って説明します。

SPIデバイスを使える状態にする

Raspberry pi 2のSPIバスは、最初封印された状態なので使えません。/boot/config.txtに以下の記載を追加して、ラズパイごと再起動する必要があります。以下の記載は本家のフォーラムに出ていた話題なのですが、Raspberry pi 2とカーネル3.18が出てから必要になったんだとか。ついでにI2Cバスも有効にしていますが、ここでは説明はしません。

# Added by myself
# http://www.raspberrypi.org/forums/viewtopic.php?p=675658#p675658
dtparam=i2c_arm=on
dtparam=spi=on

ネットを探すと/etc/modprobe.d/raspi-blacklist.confを編集する必要がある、というような記載も多々見つかるのですが、私の環境にはこのファイルはありませんでした。でも、この設定で動いています。

実際にSPIが有効になったかどうかどうかは、以下のコマンドでspidev0.0とspidev0.1のデバイスファイルが存在しているかどうかで確認できます。

pi@raspberrypi:~/work$ ls -l /dev/spi*
crw-rw---T 1 root spi 153, 0  1月  1  1970 /dev/spidev0.0
crw-rw---T 1 root spi 153, 1  1月  1  1970 /dev/spidev0.1
pi@raspberrypi:~/work$

Pythonモジュールpy-spidevの準備

SPIバスからデータを取得するプログラムをPythonで書くわけですが、それにはpy-spidevというモジュールが必要です。だいたい以下のような手順です(おおざっぱですが)。

 $ cd ~
 $ git clone git://github.com/doceme/py-spidev 
 $ cd py-spidev
 $ sudo python setup.py install

Pythonスクリプトから呼び出せるようになったかどうか確認するには、以下のようにPythonインタラクティブに呼び出してエラーがでなければOKです。

pi@raspberrypi:~$ python
Python 2.7.3 (default, Mar 18 2014, 05:13:23)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import spidev
>>>
>>>
pi@raspberrypi:~$

Pythonスクリプトを書こう

さて、いよいよ本番、センサーを読むプログラムを書きます。実はこちらのサイトにあるスクリプトを参考にさせてもらいました。ただ、最終的にはかなり書き換えています。

  1. 使っている温度センサーは別物ですので、計算を書き換え
  2. Ctrl+Cで終了したときにSPIバスが開かれたままになってしまうことを避けるため、最後の掃除用の処理を追加
  3. 温度センサーMCP9700の精度はかなり悪い(±4℃)ので、毎秒1回センサーのデータを読んで、60回分の平均をとり、それを室温として記録
  4. 室温の記録はカレントディレクトリのdata.tsvにタブ区切り(Tab Separated Value)形式で保存
  5. ついでに明度センサ(CDSセンサ)の値もとっていますが、変換式は適当です
pi@raspberrypi:~/work$ cat my_mcp3008_tmp36.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

import spidev
import time
from collections import deque
import signal
import sys
import RPi.GPIO as GPIO
from datetime import date
from datetime import datetime

# センサーがつながっているMCP3008のチャネル
cds_channel   = 0
temp_channel  = 1

# LEDが接続されているGPIOポートの番号
led = 18

# キューに保存するデータの最大数
max_data = 60

# SPIバスへのアクセスを開く
spi = spidev.SpiDev()
spi.open(0,0)

def ReadChannel(channel):
  """
  MCP3008経由でアナログセンサからのデータを受け取る。
  channelはMCP3008の入力チャンネルで、0から7の値
  """
  adc = spi.xfer2([1,(8+channel)<<4,0])
  data = ((adc[1]&3) << 8) + adc[2]
  return data

def ConvertVolts(data, places):
  """
  MCP3008から受け取ったデジタルデータを、アナログセンサの
  出力電圧に変換する計算をする。placesは有効桁数。

  """
  volts = (data * 3.3) / float(1023)
  volts = round(volts, places)
  return volts

def ConvertTemp(volts, places):
  """
  電圧をセンサーが検知した温度に変換する。
  """
  temp = ( 100 * volts ) - 50.0
  temp = round( temp, places )
  return temp

def exit_handler(signal, frame):
  """
  Ctrl+Cが押されたときにデバイスを初期状態に戻して終了する。
  """
  print("\nExit")
  spi.close()
  GPIO.cleanup()
  sys.exit(0)

#
# メインルーチン
#

# 終了時に処理するシグナルハンドラを準備
signal.signal(signal.SIGINT, exit_handler)

# LEDを点灯するためのGPIOを用意
GPIO.setmode(GPIO.BCM)
GPIO.setup(led, GPIO.OUT)

# データ保管用のキューを用意
queue = deque()

# ログファイルの用意
# tsvfile = open('data'+datetime.now().strftime("%Y%m%d_%H%M%S")+'.tsv', 'a')
tsvfile = open('data.tsv', 'a')
data_count = 0

while True:

  # LEDを点灯する
  GPIO.output(led, True)
  time.sleep(0.2)

  # 温度センサーを読む
  temp = ConvertTemp( ConvertVolts( ReadChannel( temp_channel ), 4), 4)

  # 明度センサーを読む
  cds  = ConvertTemp( ConvertVolts( ReadChannel( cds_channel ), 4), 4)

  # 平均値を出すためにデータを保存しておく
  # 一定の数データが溜まったら、古い物から削除
  queue.append((temp, cds))
  if len(queue) > max_data:
    queue.popleft()

  # 平均値を求める
  sum1 = 0.0
  sum2 = 0.0
  for d in queue:
        sum1 += d[0]
        sum2 += d[1]
  ave_temp = sum1 / len(queue)
  ave_cds  = sum2 / len(queue)

  # コンソールへ結果を表示
  print "(温度, 明度)=(%6.2f, %6.2f) 平均 = (%6.2f, %6.2f)" % (temp, cds, ave_temp, ave_cds)

  # 一定の回数ごとに平均値を記録
  data_count += 1
  if data_count == max_data:
    d = datetime.now()
    tsvfile.write("%s\t%6.2f\t%6.2f\n" % (datetime.now().strftime("\"%Y/%m/%d %H:%M:%S\""), ave_temp, ave_cds))
    tsvfile.flush()
    data_count = 0

  # LED消灯する
  GPIO.output(led, False)
  time.sleep(0.8)

pi@raspberrypi:~/work$

さて、実行

前回も書きましたが、ハードウェアに直接アクセスするプログラムを実行するには、管理者権限が必要です。sudoして実行しましょう。

pi@raspberrypi:~/work$ chmod +x my_mcp3008_tmp36.py
pi@raspberrypi:~/work$ sudo ./my_mcp3008_tmp36.py

明度(CDS)センサが気になる人がいるかもしれないので一応写真を挙げておきます。f:id:WindVoice:20150304165100j:plain

取得された室温データは、以下のような形式になっていて、Excelなどで利用することもできます。

pi@raspberrypi:~/work$ more data20150304_153031.tsv
"2015/03/04 15:31:30"    23.92  247.36
"2015/03/04 15:32:30"    23.86  246.96
pi@raspberrypi:~/work$

次回は取得したデータをグラフにするところを書こうと思います。

Raspberry Pi 2 Model B (1)

Raspberry Pi 2 Model B (1)