TWELITEからシリアル通信で送られてきたデータを取得できました。次はこのデータを保存します。テキストで保存するのか違う岸城にするのか?一番簡単なのはテキスト形式です。
CSV形式で保存すれば表計算ソフトで表やグラフで見ることができます。しかしテキスト形式の弱点はファイルが大きくなってしまうことです。温度・湿度・気圧を1秒に1回送ってきます。1日分のデータは60X60X24=86400になります。1回のデータは、時刻、温度、湿度、気圧改行とします。12:56:34,24,45,1013改行 20バイトくらい必要です。1日のデータ量は1,728,000です。1日に1.7MBはラスベリーパイにはキツイ?
そこでファイルが大きくならないように独自形式での保存方法を紹介します。
ファイル拡張子:dat
世間によくある拡張子です。ただし、皆独自の形式になっているので該当するソフトがないと正しい表示や編集ができません。
ファイル形式:ヘッダー部とデータ部で構成
ヘッダー部:bme280 何のデータなのか識別するため。

データ部:温度<2バイト>湿度<2バイト>気圧<2バイト> 計6バイト
時刻はデータの位置によりきまる。1秒ごとに保存するほど変化の大きなデータではないので10秒に1回保存する。データの先頭はは00:00:00。最後は23:59:50。

ファイルのタイムスタンプは14:33で秒は不明。00秒として計算するとアドレスは0x7ACA。
赤線で囲んだところは20秒目のデータ→14:33:20のデータです。 データはリトルエンディアンで処理しているので、 最初の2バイト
10 00 →0x0010 16度 次の2バイト 00 2D →0x002D
45% 最後の2バイト E9 03 →0x03E9 1001ヘクトパスカル 測定値がファイルに保存されています。
このようなバイナリーファイルを確認するのは大変です。バイナリーエディタを使うと楽ちんです。現在使っているのはGHexです。インストールは簡単です。
sudo apt install ghex で終わりです。
ソース
# -*- coding: utf-8 -*-
import serial
import threading
import os
from datetime import datetime
import array
g_Serial = None
g_rxData = None
g_bRxFinish = False
g_bQuit = False
#記録は10秒に1回
#ファイル名はyyyymm.dd.dat
#ファイルにヘッダーをつける 'bme280'
g_DataSize = 6 * 60 * 24
g_Header = b'bme280'
def isHeader(fd):
lenght = len(g_Header)
header = os.read(fd, lenght)
if(header == g_Header):
return True
return False
def saveToFile():
global g_DataSize, g_Header
now = datetime.now()
if((now.second % 10) != 0):
return
date = "%04d%02d%02d" %(now.year, now.month, now.day)
filename = '/media/pi/USBMEMO/' + date + '.dat'
print(filename)
ary = array.array('b',[-1, -1, -1, -1, -1, -1] * g_DataSize)
# print(ary)
fd = None
if(os.path.exists(filename) == True):
fd = os.open(filename, os.O_RDWR)
if(isHeader(fd) == False):
os.close(fd)
return
#ary = os.read(fd, 6 * g_DataSize)# 6 byte * size
else:
fd = os.open(filename, os.O_RDWR|os.O_CREAT )
os.write(fd, g_Header)
os.write(fd, ary)
writePos = int(len(g_Header) + (now.second / 10) * 6 + now.minute * 6 * 6 + now.hour * 6 * 6 * 60)
os.lseek(fd, writePos, os.SEEK_SET)
print(writePos)
uRx = g_rxData.decode()
#余計なスペースを削除
uRx.replace(' ', '')
#温度データを整数型にする
posStart = uRx.find('T=')
posEnd = uRx.find('H=')
temprature = int(uRx[posStart + 2 : posEnd])
#リトルエンディアンに変換して書き込み
little = int.to_bytes(temprature, 2, 'little')
os.write(fd, little)
#湿度データを整数型にする
posStart = uRx.find('H=')
posEnd = uRx.find('P=')
humidity = int(uRx[posStart + 2 : posEnd])
#リトルエンディアンに変換して書き込み
little = int.to_bytes(humidity, 2, 'little')
os.write(fd, little)
#気圧データを整数型にする
posStart = uRx.find('P=')
posEnd = uRx.find('\n')
pressure = int(uRx[posStart + 2 : posEnd])
#リトルエンディアンに変換して書き込み
little = int.to_bytes(pressure, 2, 'little')
os.write(fd, little)
os.close(fd)
#RX232C受信スレッド関数
def rx232c():
global g_bRxFinish, g_rxData, g_bQuit
while(True):
if(g_bQuit == True):
break
rxData = g_Serial.read()
if(rxData == b'\n'):
g_bRxFinish = True
else:
if(g_rxData == None):
g_rxData = rxData
else:
g_rxData += rxData
def isRxData():
global g_rxData
if(g_rxData == None):
return False
if(g_rxData.find(b'T=') == -1):
return False
if(g_rxData.find(b'H=') == -1):
return False
if(g_rxData.find(b'P=') == -1):
return False
return True
def main():
global g_Serial,g_bRxFinish, g_rxData, g_bQuit
g_Serial = serial.Serial('/dev/ttyUSB0', 115200)
threadRx = threading.Thread(target=rx232c)
threadRx.start()
num = 1
try:
while(g_bQuit == False):
if(g_bRxFinish == True):
g_bRxFinish = False
if(isRxData() == True):
print('{:4d} {}'.format(num, g_rxData))
else:
print('RX data is invalid.')
saveToFile()
g_rxData = None
g_rxData = b''
l = len(g_rxData)
num += 1
except KeyboardInterrupt:
#print('control C')
g_bQuit = True
threadRx.join()
g_Serial.close()
if __name__ == "__main__":
main()
シリアル通信データを受信するとsaveToFile()が呼び出されます。この関数でやっていることは10秒に一回ファイルにデータを保存する。
保存するファイル名はyyyymmdd.datです。 ファイルがないときはヘッダー部と0xFFのデータ部をファイルに書き込みます。
時;分秒からファイルの書込み位置を算出して、そこに温度・湿度・気圧をリトルエンディアンに 変換して書き込みます。
今回はコメントを多くしたので参考にしてください。
|