#モチベーション
センサを使った何かをやってみたかったから、温度・湿度・気圧あたりでも計測してみるか。
#使用したもの
・RaspberryPiZeroW
・BME280センサモジュール(ピンヘッダ実装済み)
#仕様
・5分に1回、温度・湿度・気圧を測ってcsvで直近50回のデータを保存しておく。
・csvからグラフを描画してpng画像として保存しておく。
・png画像をWebサーバにあげてどこからでも確認できるようにする。
・うろ覚えだけど、たしかI2Cを有効にする設定をしないといけなかった気がするので、その辺は各自調べてくださいね。
#事前準備
I2Cの有効化とPythonでI2Cを扱うための準備をするよ。
##I2C有効化
pi@raspberrypi:~ $sudo raspi-config
##必要なモジュールのダウンロード
pi@raspberrypi:~ $sudo apt-get install i2c-tools
pi@raspberrypi:~ $sudo apt-get install python-smbus
準備終了。
#ソースコード(計測・csv保存)
参考にリンクを貼っておいたけど、スイッチサイエンスさんがサンプルコードを公開しているのでそれに付け足していく。
直近50回分(5分*50回=250分前まで)の計測データだけcsvでそれぞれ保存してる。
形式は「年,月,日,時,分,計測値\n」
csvの保存先は/var/www/html/
#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軸が指数表記になってしまうことがあったのを最近修正しました。
#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!")
#参考
BME280搭載 温湿度・気圧センサモジュール ピンヘッダ実装済
BME280サンプルコード
matplotlibで軸のオフセット表記を禁止させる
fritzingで使えるbme280のパーツ