LoginSignup
6
10

More than 3 years have passed since last update.

ラズパイで温度湿度気圧を測る

Last updated at Posted at 2017-12-02

モチベーション

センサを使った何かをやってみたかったから、温度・湿度・気圧あたりでも計測してみるか。

使用したもの

・RaspberryPiZeroW
・BME280センサモジュール(ピンヘッダ実装済み)

仕様

・5分に1回、温度・湿度・気圧を測ってcsvで直近50回のデータを保存しておく。
・csvからグラフを描画してpng画像として保存しておく。
・png画像をWebサーバにあげてどこからでも確認できるようにする。
・うろ覚えだけど、たしかI2Cを有効にする設定をしないといけなかった気がするので、その辺は各自調べてくださいね。

事前準備

I2Cの有効化とPythonでI2Cを扱うための準備をするよ。

I2C有効化

I2C有効化
pi@raspberrypi:~ $sudo raspi-config

2019-09-08 (2).png
「5」を選択

2019-09-08 (3).png
「P5」を選択

2019-09-08 (4).png
「Yes」を選択

2019-09-08 (5).png
「Ok」で完了

必要なモジュールのダウンロード

2つダウンロード
pi@raspberrypi:~ $sudo apt-get install i2c-tools
pi@raspberrypi:~ $sudo apt-get install python-smbus

準備終了。

ブレッドボード

bme280_ブレッドボード.png

回路図

bme280_回路図.png

ソースコード(計測・csv保存)

参考にリンクを貼っておいたけど、スイッチサイエンスさんがサンプルコードを公開しているのでそれに付け足していく。
直近50回分(5分*50回=250分前まで)の計測データだけcsvでそれぞれ保存してる。
形式は「年,月,日,時,分,計測値\n」
csvの保存先は/var/www/html/

bme280_sample.py

#Save the temperature, humidity, and atmospheric pressure obtained from bme280 in csv
#csv format:
#year,month,date,hour,min,val
#coding: utf-8
#2017/09/23
#2017/10/26

from smbus import SMBus
import time
import csv
import datetime

bus_number  = 1
i2c_address = 0x76
MAX_DATA=50

bus = SMBus(bus_number)

digT = []
digP = []
digH = []

t_fine = 0.0

d=datetime.datetime.today()

def writeReg(reg_address, data):
    bus.write_byte_data(i2c_address,reg_address,data)

def get_calib_param():
    calib = []

    for i in range (0x88,0x88+24):
        calib.append(bus.read_byte_data(i2c_address,i))
    calib.append(bus.read_byte_data(i2c_address,0xA1))
    for i in range (0xE1,0xE1+7):
        calib.append(bus.read_byte_data(i2c_address,i))

    digT.append((calib[1] << 8) | calib[0])
    digT.append((calib[3] << 8) | calib[2])
    digT.append((calib[5] << 8) | calib[4])
    digP.append((calib[7] << 8) | calib[6])
    digP.append((calib[9] << 8) | calib[8])
    digP.append((calib[11]<< 8) | calib[10])
    digP.append((calib[13]<< 8) | calib[12])
    digP.append((calib[15]<< 8) | calib[14])
    digP.append((calib[17]<< 8) | calib[16])
    digP.append((calib[19]<< 8) | calib[18])
    digP.append((calib[21]<< 8) | calib[20])
    digP.append((calib[23]<< 8) | calib[22])
    digH.append( calib[24] )
    digH.append((calib[26]<< 8) | calib[25])
    digH.append( calib[27] )
    digH.append((calib[28]<< 4) | (0x0F & calib[29]))
    digH.append((calib[30]<< 4) | ((calib[29] >> 4) & 0x0F))
    digH.append( calib[31] )

    for i in range(1,2):
        if digT[i] & 0x8000:
            digT[i] = (-digT[i] ^ 0xFFFF) + 1

    for i in range(1,8):
        if digP[i] & 0x8000:
            digP[i] = (-digP[i] ^ 0xFFFF) + 1

    for i in range(0,6):
        if digH[i] & 0x8000:
            digH[i] = (-digH[i] ^ 0xFFFF) + 1  

def readData():
    data = []
    for i in range (0xF7, 0xF7+8):
        data.append(bus.read_byte_data(i2c_address,i))
    pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
    temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
    hum_raw  = (data[6] << 8)  |  data[7]

    compensate_T(temp_raw)
    compensate_P(pres_raw)
    compensate_H(hum_raw)

def compensate_P(adc_P):
    global  t_fine
    pressure = 0.0
    ary_pressure=["0,0,0,0,0,0\n" for i in range(MAX_DATA)]

    v1 = (t_fine / 2.0) - 64000.0
    v2 = (((v1 / 4.0) * (v1 / 4.0)) / 2048) * digP[5]
    v2 = v2 + ((v1 * digP[4]) * 2.0)
    v2 = (v2 / 4.0) + (digP[3] * 65536.0)
    v1 = (((digP[2] * (((v1 / 4.0) * (v1 / 4.0)) / 8192)) / 8)  + ((digP[1] * v1) / 2.0)) / 262144
    v1 = ((32768 + v1) * digP[0]) / 32768

    if v1 == 0:
        return 0
    pressure = ((1048576 - adc_P) - (v2 / 4096)) * 3125
    if pressure < 0x80000000:
        pressure = (pressure * 2.0) / v1
    else:
        pressure = (pressure / v1) * 2
    v1 = (digP[8] * (((pressure / 8.0) * (pressure / 8.0)) / 8192.0)) / 4096
    v2 = ((pressure / 4.0) * digP[7]) / 8192.0
    pressure = pressure + ((v1 + v2 + digP[6]) / 16.0)  

    print("pressure : %7.2f hPa" % (pressure/100))

    fp=open("/var/www/html/pressure.csv","rb")
    line=fp.readline()
    while line:
        ary_pressure.pop(0)
        ary_pressure.append(line)
        line=fp.readline()
    ary_pressure.pop(0)
    ary_pressure.append(str(d.year)+","+str(d.month)+","+str(d.day)+","+str(d.hour)+","+str(d.minute)+","+str(pressure/100)+"\n")
    fp.close()

    fp=open("/var/www/html/pressure.csv","w")
    fp.writelines(ary_pressure)
    fp.close()

def compensate_T(adc_T):
    global t_fine
    ary_temp=["0,0,0,0,0,0\n" for i in range(MAX_DATA)]
    v1 = (adc_T / 16384.0 - digT[0] / 1024.0) * digT[1]
    v2 = (adc_T / 131072.0 - digT[0] / 8192.0) * (adc_T / 131072.0 - digT[0] / 8192.0) * digT[2]
    t_fine = v1 + v2
    temperature = t_fine / 5120.0
    print("temp : %-6.2f " % (temperature))

    fp=open("/var/www/html/temp.csv","rb")
    line=fp.readline()
    while line:
        ary_temp.pop(0)
        ary_temp.append(line)
        line=fp.readline()
    ary_temp.pop(0)
    ary_temp.append(str(d.year)+","+str(d.month)+","+str(d.day)+","+str(d.hour)+","+str(d.minute)+","+str(temperature)+"\n")
    fp.close()

    fp=open("/var/www/html/temp.csv","w")
    fp.writelines(ary_temp)
    fp.close()

def compensate_H(adc_H):
    global t_fine
    ary_hum=["0,0,0,0,0,0\n" for i in range(MAX_DATA)]
    var_h = t_fine - 76800.0
    if var_h != 0:
        var_h = (adc_H - (digH[3] * 64.0 + digH[4]/16384.0 * var_h)) * (digH[1] / 65536.0 * (1.0 + digH[5] / 67108864.0 * var_h * (1.0 + digH[2] / 67108864.0 * var_h)))
    else:
        return 0
    var_h = var_h * (1.0 - digH[0] * var_h / 524288.0)
    if var_h > 100.0:
        var_h = 100.0
    elif var_h < 0.0:
        var_h = 0.0
    print("hum : %6.2f %%" % (var_h))

    fp=open("/var/www/html/hum.csv","rb")
    line=fp.readline()
    while line:
        ary_hum.pop(0)
        ary_hum.append(line)
        line=fp.readline()
    ary_hum.pop(0)
    ary_hum.append(str(d.year)+","+str(d.month)+","+str(d.day)+","+str(d.hour)+","+str(d.minute)+","+str(var_h)+"\n")
    fp.close()

    fp=open("/var/www/html/hum.csv","w")
    fp.writelines(ary_hum)
    fp.close()

def setup():
    osrs_t = 1          #Temperature oversampling x 1
    osrs_p = 1          #Pressure oversampling x 1
    osrs_h = 1          #Humidity oversampling x 1
    mode   = 3          #Normal mode
    t_sb   = 5          #Tstandby 1000ms
    filter = 0          #Filter off
    spi3w_en = 0            #3-wire SPI Disable

    ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode
    config_reg    = (t_sb << 5) | (filter << 2) | spi3w_en
    ctrl_hum_reg  = osrs_h

    writeReg(0xF2,ctrl_hum_reg)
    writeReg(0xF4,ctrl_meas_reg)
    writeReg(0xF5,config_reg)


setup()
get_calib_param()


if __name__ == '__main__':
    try:
        readData()
    except KeyboardInterrupt:
        pass

ソースコード(png画像生成・サーバに送信)

FTPサーバにpng画像あげて、自宅にいなくても自宅の温度とかがわかる。
温度が低すぎたり、湿度が不快だったらLINEにメッセージを飛ばす。
(LINEにメッセージを飛ばすことについては解説しません。)
こちらも生成した画像は/var/www/html/に保存。
グラフの描画にはmatplotlibを使ってる。

修正箇所

・ラベルが見切れてしまう問題を修正しました。
・気圧があまり変化しなかったとき、y軸が指数表記になってしまうことがあったのを最近修正しました。

env_graph.py
#Graph the temperature, humidity, pressure and upload it to the FTP server.
#coding: utf-8
#2017/09/23
#2017/10/04
#2017/10/26

import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import csv
from pylab import *
from ftplib import FTP
import re
import requests
import os

from linebot import LineBotApi
from linebot.models import TextSendMessage

MAX_RANGE=50
URL_PATH="/var/www/html/"

def drawingTemp():

    temp=[0 for i in range(MAX_RANGE)]
    date=[0 for i in range(MAX_RANGE)]
    span=[]

    for i in range(MAX_RANGE):
        span.append(1000/MAX_RANGE*i)

    fp=open(URL_PATH+"temp.csv","rb")
    reader=csv.reader(fp)
    for row in reader:
        temp.pop(0)
        temp.append(float(row[5]))
        date.pop(0)
        #month/date_hour:minute
        date.append("%02d"%int(row[1])+"/"+"%02d"%int(row[2])+"_"+"%02d"%int(row[3])+":"+"%02d"%int(row[4]))
    fp.close()

    if(temp[MAX_RANGE-1]<=20.0):
        sendMsgToLine("WARNING:temp is under 20C!!")

    fig=plt.figure(figsize=(16,9))
    ax=fig.add_subplot(1,1,1)
    plt.title("Temperature")
    plt.gca().get_yaxis().get_major_formatter().set_useOffset(False)
    plt.xlabel("month/date_hour:minute")
    plt.ylabel("degree Celsius")
    plt.plot(temp,color="m")
    plt.xticks(range(MAX_RANGE),date)
    grid(True)
    labels=ax.set_xticklabels(date,rotation=90,fontsize="small")

    plt.savefig(URL_PATH+"temp.png",bbox_inches="tight")

def drawingPressure():

    pressure=[0 for i in range(MAX_RANGE)]
    date=[0 for i in range(MAX_RANGE)]
    span=[]

    for i in range(MAX_RANGE):
        span.append(1000/MAX_RANGE*i)

    fp=open(URL_PATH+"pressure.csv","rb")
    reader=csv.reader(fp)
    for row in reader:
        pressure.pop(0)
        pressure.append(float(row[5]))
        date.pop(0)
        #month/date_hour:minute
        date.append("%02d"%int(row[1])+"/"+"%02d"%int(row[2])+"_"+"%02d"%int(row[3])+":"+"%02d"%int(row[4]))
    fp.close()

    #Warn if the air pressure acquired in the last 6 times keeps falling
    flg=True
    for i in range(MAX_RANGE-6,MAX_RANGE-1):
        if(pressure[i]<pressure[i+1]):
            flg=False
            break
    if(flg):
        sendMsgToLine("WARNING:Barometric pressure keeps falling for 30 consecutive minutes")

    fig=plt.figure(figsize=(16,9))
    ax=fig.add_subplot(1,1,1)
    plt.title("Pressure")
    plt.gca().get_yaxis().get_major_formatter().set_useOffset(False)
    plt.xlabel("month/date_hour:minute")
    plt.ylabel("Hectopascals")
    plt.plot(pressure,color="c")
    plt.xticks(range(MAX_RANGE),date)
    grid(True)
    labels=ax.set_xticklabels(date,rotation=90,fontsize="small")

    plt.savefig(URL_PATH+"pressure.png",bbox_inches="tight")

def drawingHum():

    hum=[0 for i in range(MAX_RANGE)]
    date=[0 for i in range(MAX_RANGE)]
    span=[]

    for i in range(MAX_RANGE):
        span.append(1000/MAX_RANGE*i)

    fp=open(URL_PATH+"hum.csv","rb")
    reader=csv.reader(fp)
    for row in reader:
        hum.pop(0)
        hum.append(float(row[5]))
        date.pop(0)
        #month/date_hour:minute
        date.append("%02d"%int(row[1])+"/"+"%02d"%int(row[2])+"_"+"%02d"%int(row[3])+":"+"%02d"%int(row[4]))
    fp.close()

    if(hum[MAX_RANGE-1]>=70.0):
        sendMsgToLine("WARNING:Hum is over 70%!!")

    if(hum[MAX_RANGE-1]<40.0):
        sendMsgToLine("WARNING:Hum is under 40%!!")

    fig=plt.figure(figsize=(16,9))
    ax=fig.add_subplot(1,1,1)
    plt.title("Humidity")
    plt.gca().get_yaxis().get_major_formatter().set_useOffset(False)
    plt.xlabel("month/date_hour:minute")
    plt.ylabel("percent")
    plt.plot(hum,color="g")
    plt.xticks(range(MAX_RANGE),date)
    grid(True)
    labels=ax.set_xticklabels(date,rotation=90,fontsize="small")

    plt.savefig(URL_PATH+"hum.png",bbox_inches="tight")

def sendMsgToLine(msg):
    Channel_Access_Token="XXXXXXXXXX"
    User_Id="XXXXXXXXXX"
    Line_Bot_Api=LineBotApi(Channel_Access_Token)
    Line_Bot_Api.push_message(User_Id,TextSendMessage(text=msg))

print("start drawing Temp...")
drawingTemp()
print("successful")

print("start drawing Pressure...")
drawingPressure()
print("successful")

print("start drawing Hum...")
drawingHum()
print("successful")

print("connect to http server...")
ftp=FTP("FTP_Server","usrid","passwd")
print("successful")

#only first time
#send to html
#fp=open("../../var/www/html/index.html","rb")
#ftp.storbinary("STOR public_html/index.html",fp)
#fp.close()

#send temp to http
print("send temp.png to http server...")
fp=open(URL_PATH+"temp.png","rb")
ftp.storbinary("STOR public_html/temp.png",fp)
fp.close()
print("successful")

#send pressure to http
print("send pressure.png to http server...")
fp=open(URL_PATH+"pressure.png","rb")
ftp.storbinary("STOR public_html/pressure.png",fp)
fp.close()
print("successful")

#send hum to http
print("send hum.png to http server...")
fp=open(URL_PATH+"hum.png","rb")
ftp.storbinary("STOR public_html/hum.png",fp)
fp.close()
print("successful")

ftp.close()
print("all complete!")

実行結果

俺の部屋の環境はこうです。
温度
temp.png
気圧
pressure.png
湿度
hum.png

参考

BME280搭載 温湿度・気圧センサモジュール ピンヘッダ実装済
BME280サンプルコード
matplotlibで軸のオフセット表記を禁止させる
fritzingで使えるbme280のパーツ

6
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
10