Raspberry で気温と湿度を監視しよう（DHT11）

Raspberry と言えば、電子工作ですよね。
Ｌチカは、「[ScratchでＬチカ](https://qiita.com/MasahiroShouya/items/9ffcffcc15effb91caa9)」や、「[LチカをWebで制御する](https://qiita.com/MasahiroShouya/items/9c849331046f2665b913)」の形で掲載したので、今回は温湿度がターゲットです。

**今回の目標**
DHT11で取得した温湿度を、データベース（Postgres）に記録し、照会する機能（Streamlit）を準備します。

**利用する機材**
① DHT11（基盤に乗っている扱いの簡単な機材を選択しました）
② ジャンパー線３本　（メス・メス）

**DHT11**
DHT11というと青い格子に足４本ですが、使い易い状態で基盤にはんだ付けされた物もあります。青い格子に足４本ですと、抵抗を入れる必要がある、不要な足がある等、扱いが少し面倒なので、便利な既製品を御薦めします。（回路作りを楽しみたい方は、青い格子に足4本で頑張りましょう）
データシートで性能を確認する限り、測定誤差が大きく御遊び感多々です。
私が選択した基盤にはんだ付けされた物ですが、足は３本で、
　VCC　／　DATA　／　GND
と記載があります。色々な電材で必要な知識ですので覚えましょう。

**電材のデータシート**
電材の説明書みたいなものです。（英語が多く面倒ですが…）
性能や必要となる電圧等、利用に重要な内容が記載されていますので、利用の際には、必ず確認しましょう。

**Raspberry Pi の準備**
Raspberry Pi OS は Lite を利用します。
Postgresを使いたいので、以前の [安価なWeb開発勉強環境の構築](https://qiita.com/MasahiroShouya/items/691601d10a85f4bae371) を参照して、インストールを進めます。
SQLAlchemy まで準備し、テーブルの作成は、下記で準備しましょう。
```diff_sql:テーブル
create table t_data (
    kizai varchar(10),
    nitiji timestamp,
    ondo numeric(4,2),
    situdo numeric(4,2),
    kiatu numeric(6,2),
primary key ( kizai, nitiji ) );
```
他には、以下で準備してみます。
sudo mkdir /test　（作業用ディレクトリ作成）
cd /test　（カレント移動）
sudo python3 -m venv venv　（仮想準備）
sudo chmod 777 -R /test　（扱いやすいように権限を落とす）
source /test/venv/bin/activate　（仮想環境起動）
pip install psycopg2-binary　（Postgresコネクターインストール）
pip3 install SQLAlchemy　（SQLAlchemyインストール）
他に必要な手順としては、venvの仮想でGPIOを利用したいので、「[ScratchでＬチカ](https://qiita.com/MasahiroShouya/items/9ffcffcc15effb91caa9)」の「余談」対応が必要です。
sudo apt-get install pip　（デフォルトのpipを補填します）
pip install RPi.GPIO　（仮想環境にGPIOのパッケージを用意します）
Git-Hub に公開されているプログラムを利用させて頂きたいと思います。
sudo apt-get install git　（Gitの利用の為インストール）
git clone https://github.com/szazo/DHT11_Python.git　（Gitよりクローン）
「/test/DHT11_Python」が作成されているはずです。この中に、example.py のサンプルプログラムが用意されています。
サンプルプログラムを見ると、下記のように、GPIO-14 を利用しているようです。

```diff_python:example.py
# read data using pin 14
instance = dht11.DHT11(pin=14)
```

このプログラムを、そのまま利用したいと思います。
vcc（電源）2番ピン（5v）と、data（情報の制御用）8番ピン（GPIO-14）と、GNDは数か所ありますが 14番ピンを使ってみましょう。
cd /test/DHT11_Python　（カレント移動）
pyhon example.py　（プログラム実行）

Last valid input: 2024-11-03 14:51:25.319791
Temperature: 24.5 C
Humidity: 53.0 %

のような処理結果が、６秒置きに表示されるはずです。＜ここまで出来れば、現実世界の準備は完了です＞
「CTRL＋C」でプログラムを終了させます。　次にプログラム（example.py）を修正して、今回のお題を実現するプログラムを準備します。
まずは、Postgres へのアクセスを SQLAlchemy で操作するプログラムを用意します。

```diff_python:db.py　（データベース操作用プログラム）
from sqlalchemy import create_engine, Column, Integer, String, DateTime, Float
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
engine = create_engine('postgresql://flask1:パスワード@localhost:5432/flask1')
Base = declarative_base()
Ses = sessionmaker(bind=engine)
class t_data(Base):
    __tablename__ = 't_data'
    kizai = Column(String(10), primary_key=True)
    nitiji = Column(DateTime)
    ondo = Column(Float)
    situdo = Column(Float)
    kiatu = Column(Float)
```
cp example.py dht11p.py　（サンプルプログラムをコピー作成して編集します）

```diff_python:dht11p.py
import RPi.GPIO as GPIO
import dht11
import time
import datetime
import db
GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM)
instance = dht11.DHT11(pin=14)
dtCtrl = db.Ses()
try:
        while True:
            result = instance.read()
            if result.is_valid():
                print("Last valid input: " + str(datetime.datetime.now()))
                print("Temperature: %-3.1f C" % result.temperature)
                print("Humidity: %-3.1f %%" % result.humidity)
                newDt = db.t_data(
                    kizai='DHT11',
                    nitiji=datetime.datetime.now(),
                    ondo=result.temperature,
                    situdo=result.humidity,
                    kiatu=0
                )
                dtCtrl.add(newDt)
                dtCtrl.commit()
                dtCtrl.close()
                break
            time.sleep(6)
except KeyboardInterrupt:
    print("Cleanup")
    GPIO.cleanup()
```
一度実行してみましょう。
python dht11p.py　（プログラム実行）
先程と同じように、時間・温度 が表示されて、今回はプログラムが終了するはずです。
これを、１分おきに、自動実行するように設定していきます。
sudo crontab -e　（ジョブ設定に下記を追加します）
```diff_conf:cron
* * * * * /bin/bash -c 'source /test/venv/bin/activate && python /test/DHT11_Python/dht11p.py'
```

ここから、以前の記事「[Raspberryでstreamlitを勉強してみる②](https://qiita.com/MasahiroShouya/items/78be659c1931f43cde17)」で照会機能を準備します。
今回はサイドバーは、無しで簡単なプログラムを準備します。

```diff_conf:graph.py
import streamlit as st
from sqlalchemy import create_engine, select, text

engine = create_engine('postgresql://flask1:パスワード@localhost:5432/flask1')
with engine.connect() as conn:
    stmt = select('*').select_from(text('t_data'))
    result = conn.execute(stmt)
    data = result.fetchall()

import pandas as pd
df = pd.DataFrame(data, columns=['器具', '日時', '温度', '湿度', '気圧'])
df['日時'] = pd.to_datetime(df['日時'])
df.set_index('日時', inplace=True)
df['温度'] = df['温度'].astype(float)
df['湿度'] = df['湿度'].astype(float)

st.title('温度・湿度')     # 気圧はDHT11では計測できないです
st.line_chart(df[['温度', '湿度']], use_container_width=True)
```

プログラムが完成したらテスト確認しましょう。
（仮想環境のまま）streamlit run graph.py　（streamlitが実行されます）
その後、パソコンより「http://（RaspberryのIPアドレス）:8501」へアクセスします。

:::note warn
少し時間を取られた問題発生
streamlitのデフォルトportが、8501なのですが、稀に別のポートで起動されるようです。
streamlit run すると、port番号が明示されるので、確認してから起動しましょう。
:::

最後に、streamlitをサービス起動するように変更しましょう。
以前の記事「[Raspberryでstreamlitを勉強してみる①](https://qiita.com/MasahiroShouya/items/f2e37210c7d84ef062a1)」の「streamlitをサービス起動する方法」を利用します。
上記の問題で迷わないように、ポートは指定してサービス稼働したいと思います。
sudo vi /etc/systemd/system/streamlit_service.service
```diff_conf:streamlit_service.service
[Unit]
Description=Streamlit Service
After=network.target
[Service]
WorkingDirectory=/test
Environment=PATH=/test/venv/bin:$PATH
ExecStart=/test/venv/bin/streamlit run --server.port 8501 /test/DHT11_Python/graph.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
```
編集が終わったら、サービスを起動して確認します。
sudo systemctl daemon-reload
sudo systemctl start streamlit_service
問題無ければ、サービスを登録しましょう。
sudo systemctl enable streamlit_service　（サービス起動として登録）
sudo reboot　（raspberryを再起動して確認しましょう）
これで、通電したら、即計測開始＆即グラフ提供開始の実現です。
お疲れさまでした～。