chakokuのブログ(rev4)

テック・コミック・ごくまれにチャリ

AppSheetを使って部屋の温湿度を見える化

要約:温湿度センサ(BME280)の計測値をマイコンを使ってGCPにアップロード、AppSheetを使ってスマフォでグラフ表示するシステムを試作した

詳細:自分の部屋は西向きでエアコンが無い。部屋の中で熱中症になるのでは?と思われる程に暑い。前から温湿度を記録したいと思っていて、AppSheetの試作例にもちょうどいいので、温湿度の見える化をやってみた。BME280で計測した値をマイコンからGoogleDrive?のSpreadSheetに上げる際、本来はOAuthを使って認証すべきなんだけど、マイコンでOAuthは大変なので、、CloudFunctionを使って軽量のWebAPIを作成し、マイコンからはこのWebAPIにデータをPOSTする仕様とした。SpreadSheetにデータが追記されると、あとはAppSheetでグラフ化すれば部屋の温湿度の推移が見えるようになる。
システムのブロック図は以下。JS使いの人は、CloudFunctionsを使わず、GASを使ってAPIを実装すると思います

開発中のAppSheetの画面

スマフォ上の表示画面は以下。スマフォアプリを自作するとデプロイが面倒ですが、スマフォ上にAppSheetのアプリをインストールして、このアプリ上で動作するので、リリースが楽(更新操作するだけで反映される)。

8/15の場合、16:54の時点で室内の温度が36℃まで上昇している。

■ご参考
CloudFunctionsのソース(マイコンから計測データを受け取るためのWebAPI用関数)
正しく設定された場合のみ動作するコードであり、エラー処理等ほとんどなし

from googleapiclient.discovery import build
import datetime

SPREADSHEET_ID = '1g *シートID*  seM'
SHEET_NAME = "*シート名*"
RANGE = f"'{SHEET_NAME}'!A2"
SENSOR_ID = "*センサーID*"
API_KEY = "** API KEY(適当な乱数)** "

def upload(request):
    
    sensor_id = SENSOR_ID
    serial_number = ""
    temperature = 0.0
    humidity = 0.0
    pressure = 0.0
    regist_date = ""
    device_id = ""
    #print("headers....")
    #print(request.headers)

    request_json = request.get_json()
    #print(request_json)
    #print(request)
    JST = datetime.timezone(datetime.timedelta(hours=9), 'JST')
    dt = datetime.datetime.now(JST)
    jst_dt_str = dt.strftime("%Y-%m-%d %H:%M:%S")

    # check API KEY
    if "API_KEY" in request_json:
    #    print(type(request_json)) 
        api_key = request_json["API_KEY"]
    #    print(type(api_key)) 
        if api_key  == API_KEY:
            pass
        else:
            return None
    else:
        return None

    regist_date = jst_dt_str
    if "temperature" in request_json:
        temperature = request_json["temperature"]

    if "humidity" in request_json:
        humidity = request_json["humidity"]

    if "pressure" in request_json:
        pressure = request_json["pressure"]

    if "device_id" in request_json:
        device_id = request_json["device_id"]

    if "serial_number" in request_json:
        serial_number = request_json["serial_number"]

    body = {
        'values': [
            [sensor_id, temperature, humidity, pressure, serial_number, regist_date]
        ]
    }

    service = build('sheets', 'v4')  # , credentials=creds
    sheet = service.spreadsheets()
    result = sheet.values().append(spreadsheetId=SPREADSHEET_ID, range=RANGE, body=body,
                               valueInputOption="USER_ENTERED",
                               insertDataOption="INSERT_ROWS").execute()
    print(result)

    if request.args and 'message' in request.args:
        return request.args.get('message')
    elif request_json and 'message' in request_json:
        return request_json['message']
    else:
        return f'data registered'

GCP内のサービスは、Application Default Credentials(ADC)によりリソースへのアクセス権が管理できるので、SpreadSeetsAPI呼び出しの際に、credentialファイルは指定していません。

■追記
計測できるようになったとグラフ見ながら家族の面々に話をしたら、「雨が降った時間と計測グラフの山がずれている」と言われて、、確かに計測された数値が合ってるのか、家にある温湿度計と見比べる必要があると思った。(計測は30分周期なので、30分間隔でサンプリングされた結果のグラフではあるのだが)

■ご参考URL
actionの使い方解説
execute an action on a set of rows(第1回)テーブルをまたいだ処理