ラズパイの湿度、温度、気圧データをSpread sheetへ出力する。

ラズパイの湿度、温度、気圧データを取得し、spread sheetへ出力するまでをまとめてみました。
pread sheetは、google driveに置いています。

# 環境
Python version:3.9.7
OS: windows: 10.0
Linux raspberrypi: 5.10.63+

# プログラムの実行イメージ
ラズパイからgoogle driveにspread sheetを作成するまでの下準備は、下記にまとめたので参照してください。
[ラズパイからGoogle DriveへGoogle Spread Sheetを作成してみた。](https://qiita.com/yutaka_m/items/0fffcfaf319dce244b27)

プログラムは、1分毎にラズパイのデータをspread sheetへ書き込みします。google drive内で操作しているフローチャートは、下記のようにしています。spread sheetがあるかどうかを確認し、なければ新規ファイルを作成し、header部分の書き込みあを行います。存在していれば、そのステップは、省略し、ログの書き込みを行っています。1分毎のデータ取得は、crontabを使って、プログラムをたたくようにしています。なお、spread sheetは、データを取得した日付で作成するようにしています。

![Untitled Diagram (1).drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176158/1e74c3b4-dbaf-ad1c-31d0-da6cef909666.png)

spread sheetは、こんな感じに出力しています。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176158/38c2619a-ded5-d0dc-5d6f-7a032687fd9e.png)

## プログラム
プログラムは、pythonで書いています。ラズパイでは、bme280を使って湿度、温度、気圧のデータを取得しています。データを取得する部分は、bme280_custom.pyでは、行っています。メインのwrite_spread.pyファイルでは、google driveへのアクセスや取得したデータをspread sheetに書き込みを行っています。spread sheetが存在しているどうかは、アクセス権限を与えたgoogle drive のディレクトリのファイルの一覧を取得し、同じファイルが存在しているかを確認し、なければ、idを取得して、spread sheetにアクセスすることができます。なければ、フォルダのIDを使って新規にファイルを作成してからspead sheetへアクセスしています。ファイルへの追記では、1列に入力しているセルの個数を確認し、+1をして結果を記入するようにしています。

```write_spread.py
import gspread
from oauth2client.service_account import ServiceAccountCredentials
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
import pprint
from datetime import datetime as dt 
import bme280_custom

scope = ['https://spreadsheets.google.com/feeds',
         'https://www.googleapis.com/auth/drive']
json_keyfile_path = 'サービスアカウントキー.json'

credentials = ServiceAccountCredentials.from_json_keyfile_name(
    json_keyfile_path, scope)

gauth = GoogleAuth()
gauth.credentials = credentials
drive = GoogleDrive(gauth)

#parameters
header =  ['date','pressure','temperature','humidity']
exsist_date_file = False

#make a file list 
file_list = drive.ListFile().GetList()

#make a date spread sheet. ex. 210203.
now = dt.now()
filename = now.strftime('%Y%m%d')
label = now.strftime('%m/%d,%H:%M')

#if today spread sheet exist.
for f in file_list:
    if f['title'] == filename:
        print(f['title'], '   \t', f['id'])
        # gspread authentication
        gc = gspread.authorize(credentials)
        # file select in spread sheet ID.
        workbook = gc.open_by_key(f['id'])
        exsist_date_file = True
       
if exsist_date_file == False:
    folder_id = '<フォルダのID>'
    f = drive.CreateFile({
        'title': filename,
        'mimeType': 'application/vnd.google-apps.spreadsheet',
        "parents": [{"id": folder_id}]})
    f.Upload()
    # gspread authentication
    gc = gspread.authorize(credentials)
    # file select in spread sheet ID.
    workbook = gc.open_by_key(f['id'])

# select worksheet
worksheet = workbook.sheet1
# Get the written work sheet data.
records = worksheet.col_values(1)

# write header.
if len(records) == 0:
    cell_list = worksheet.range('A1:D1')
    for num in range(4):
        cell_list[num].value = header[num]
    worksheet.update_cells(cell_list)
    records = worksheet.col_values(1)

# get the log 
pres,temp,hum = bme280_custom.readData()

worksheet.update_cell(len(records)+1 ,1, label)
worksheet.update_cell(len(records)+1 ,2, pres)
worksheet.update_cell(len(records)+1 ,3, temp)
worksheet.update_cell(len(records)+1 ,4, hum)
```
bme280を使って湿度、温度、気圧のデータを取得しています。
bme280_custom.pyでは、`return p , t , h`で湿度、温度、気圧を3つの返り値にして戻すようにしています。

```bme280_custom.py
#coding: utf-8

from smbus import SMBus
import time

bus_number  = 1
i2c_address = 0x76

bus = SMBus(bus_number)

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

t_fine = 0.0


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)
	t = compensate_T(temp_raw)
	p = compensate_P(pres_raw)
	h = compensate_H(hum_raw)
	return p , t , h

def compensate_P(adc_P):
	global  t_fine
	pressure = 0.0

	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)
	return "%7.2f" % (pressure/100)

def compensate_T(adc_T):
	global t_fine
	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)
	return "%.2f" % (temperature)

def compensate_H(adc_H):
	global t_fine
	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)
	return "%.2f" % (var_h)

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

```

# crontabの設定
決まった時間にpythonのプログラムをたたくために、crontabの設定しています。
```
pi@raspberrypi:~ $ sudo crontab -l

# m h  dom mon dow   command
0-59 * * * * python3 write_spread.py
```

# リンク
下記リンクを参照させていただきました。
[gspreadライブラリの使い方まとめ！Pythonでスプレッドシートを操作する](https://tanuhack.com/library-gspread/#i-8)
[Python, PyDriveでGoogle Driveのファイルのリストを作成](https://note.nkmk.me/python-pydrive-list-file/)