ChatGPTに教えてもらうPythonプログラミング
温湿度センサーSHT30でデータ取得とグラフ表示

プログラミングを自在に操れる人は多くありません。仕事としていなければ尚更です。かといって、プログラムを全く知らなくていい時代でもなくなりました。

私もまた、全くの白紙からプログラミングコードを書くことができない一人です。これまではどうしようもなかったわけですが、ChatGPTを使うことで、コードの間違いや疑問点を探ることができるようになりました。

2022年から誰でも使用できるようになったChatGPTは、登場から考えるとだいぶ進化しています。質問して回答してもらう形式で利用するため質問力が大事なことと、出された回答が正しいかどうか、権利の侵害はないか、など検証する課題は残ります。

今回、産業用ラズベリーパイ「PL-R4」にi2cで温度管理ができるモジュールを接続して、簡単なプログラミングをしてみました。いや、ChatGPTにしてもらおうと試しました。

全くの白紙から、希望するプログラムが出来上がるのか順にご紹介していきます。

Pythonプログラム

今回利用したのはPythonプログラムです。Pythonはラズベリーパイと相性がよく、事前環境など必要なく直ぐに使えます。
他と比較すると理解しやすい部類に入ります。最初の学習にPythonはオススメです。

お借りしたのはRaspberry Pi Compute Module 4搭載の産業用ラズベリーパイです。

今回の環境

  • PL-R4 BASIC IP20(OPTポート i2c仕様)
  • Raspberry Pi OS 32bit Bullseye
  • Pythonプログラム
  • 温湿度センサー(SHT30)

少し前置きしておきます。
私はプログラミングの基礎的なことをなんとなく知っています。全く分からないわけではありません。
それでも今回のコードはイチから書けません。
もし多少でも知識があれば、回答されたコードの修正に役立つでしょう。

当然ながら、コード内容をほとんど分からなくても、おかしい箇所(エラーなど)をピンポイントで質問することにより、改善策や確認ポイントを教えてくれるので安心してください。

あまり複雑でなければ、実行した結果のエラーを潰していくことで、それなりに動作するコードになるでしょう。
トライアルアンドエラーで試してみました。

今回の最終目標

何をやらせるのか、プログラミングでいかようにも出来てしまうため、最終的に望むものを大まかに決めていきます。

温湿度センサーの利用ですから、温度と湿度をリアルタイム取得させ、その値は保存できるようにしたいと思います。
更に、表示はWebブラウザで見られるようにし、できればグラフで表示もしたいと思いました。

  • 取得した値の保存
  • Webビューでの表示
  • グラフ表示

大まかにこの3点の機能を実装することを目的とします。

最初の質問

では、早速最初の質問から始めます。次のように質問しました。
「SHT30とラズベリーパイを接続し、温度を計測するPythonプログラムを書いてください」
するとどうでしょう。必要なもの、配線例、Pythonプログラムが表示されました。

質問内容によっては、どうしても一般的な回答が多くなりがちです。今回の質問だと、特に大きな間違いは無さそうな印象です。

驚くのはコードの出力よりも、こういった必要なものや配線例まで表示してくれたことです。
ChatGPT登場時はここまで親切ではなったと記憶しています。

これは多くのWEBサイトの内容など、AIとして学習してきた結果でしょう。
もしかすると、全く同じ情報で公開されているのかもしれません。

アイコンやリスト形式、表形式を使っていたり、登場時よりも読みやすくなっていて驚きます。

I2Cを有効にする

ChatGPTの回答でも冒頭にI2Cは有効にしないといけないとあります。

SHT30はI2C通信で接続されるため、まずI2Cが有効になっている必要があります。

有効化どうか調べるコマンド

raspi-config nonint get_i2c
1

返り値が0なら有効になっていて、1なら無効のままです。最初は無効(1)になっています。

raspi-configのメニューから辿ってI2Cを有効にして再起動する。

sudo raspi-config

もしくは、次のコマンドで有効にして再起動する。

sudo raspi-config nonint do_i2c 0

SHT30をRaspberry Piに物理的に繋いだら、きちんと認識できているのか確認します。
lsコマンドでデバイスのポートを調べる。

ls /dev*i2c*
/dev/i2c-0  /dev/i2c-1

i2cdetectコマンドで確認する。

sudo i2cdetect -y 0(今回は0)

※今回のテスト機は、/dev*i2c*は/dev/i2c-0、/dev/i2c-1と二つあるため、0を指定しています。一般的には1です。
ご覧のようにアドレス44と60、68(UU)が表示されました。

 i2cdetect -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- 44 -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: 60 -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --       

デフォルト(標準)だとI2Cアドレスは0x44なので今回は標準です。アドレスはプログラムコード内で使われていましたから、もしも異なるなら後からのコードにある該当箇所を変更しないとなりません。

本来はアドレス44だけ表示されればOKなのですが、アドレス60と68にも何か表示されています。
これもChatGPTで聞けば教えてくれますよ。

少し特殊なテスト機ですから、無視してもらって構いません。60は別のデバイスも使用中で、UU表示は既に使用されていたり予約されているとUUと出ます。

ともかく、I2Cアドレスは44で、認識されていることが分かりました。

最初の質問で得られたPythonプログラムコード

最初の質問でChatGPTから生成されたPythonプログラムコードは、次のように出力されました。

import time
from smbus2 import SMBus

# SHT30のI2Cアドレス(0x44 または 0x45)
SHT30_ADDR = 0x44

# コマンド(高精度測定、クロックストレッチなし)
MEASUREMENT_CMD = [0x2C, 0x06]

def read_sht30(bus):
    # 測定コマンド送信
    bus.write_i2c_block_data(SHT30_ADDR, MEASUREMENT_CMD[0], [MEASUREMENT_CMD[1]])
    time.sleep(0.5)  # 測定待ち

    # 6バイトのデータを読み取り(温度2+CRC1、湿度2+CRC1)
    data = bus.read_i2c_block_data(SHT30_ADDR, 0x00, 6)

    # 温度データの計算
    temp_raw = data[0] << 8 | data[1]
    temperature = -45 + (175 * temp_raw / 65535.0)

    # 湿度データの計算(おまけ)
    humid_raw = data[3] << 8 | data[4]
    humidity = 100 * humid_raw / 65535.0

    return temperature, humidity

if __name__ == "__main__":
    with SMBus(1) as bus:
        while True:
            temperature, humidity = read_sht30(bus)
            print(f"温度: {temperature:.2f}°C, 湿度: {humidity:.2f}%")
            time.sleep(2)

私はRaspberry Piのサイト運営上、ある程度はPythonを扱ってきました。趣味として古くに基礎的なことは理解しているので、コードを見ても面食らうことはありません。もし初めての場合は意味が分からなくても当然です。

分からないまま、とにかく一度実行してみましょう。

そのまま実行してみる

コードをsensor.pyファイルとして保存し、コマンドから実行させました。

いきなりエラーです!

sudo python3 sensor.py 
Traceback (most recent call last):
  File "/home/pi/test/sensor.py", line 2, in <module>
    from smbus2 import SMBus

どうやらsmbus2モジュールがないためにインポートできないエラーでした。

そういえば、忘れていました。まだインストールしていませんでした。
事前条件に書いてあったsmbus2です。
pipでsmbus2-0.5.0をインストールします。

sudo pip3 install smbus2

もう一度実行してみます。
おっと、I/Oエラーで全く駄目です。

sudo python3 sensor.py 
Traceback (most recent call last):
  File "/home/pi/test/sensor.py", line 31, in <module>
    temperature, humidity = read_sht30(bus)
  File "/home/pi/test/sensor.py", line 12, in read_sht30
    bus.write_i2c_block_data(SHT30_ADDR, MEASUREMENT_CMD[0], [MEASUREMENT_CMD[1]])
  File "/usr/local/lib/python3.9/dist-packages/smbus2/smbus2.py", line 645, in write_i2c_block_data
    ioctl(self.fd, I2C_SMBUS, msg)
OSError: [Errno 121] Remote I/O error

いきなり躓きました。何がおかしいのでしょうか。
修正していくために再度ChatGPTに質問していきます。

修正を依頼する

プログラミングができる人は既に気が付いているかもしれません。
実は、今回のテスト機だと一般的な/dev/i2c-1ではなく、/dev/i2c-0を使用していたため、コード上の読み取る指定が間違っていたというわけです。

ChatGPTとしては、一般的な/dev/i2c-1を前提にしていたから、コードを提案してきました。
ですから、一般的なSMBus(1)の箇所を、SMBus(0)に置き換えれば済んだ話です。

今回のテスト機が少々特殊な作りのため、ほとんどのRaspberry PiならSMBus(1)で問題ないと思います。

with SMBus(0) as bus:  # ← ここを 0 にする

この修正案を知るために、どうやって質問したかというと、エラーをそのままコピペして送信しただけです。

多くのチェックポイントを提案してくれた中で、i2cdetectコマンドの結果から突き止められるかもしれないと思い、次にi2cdetectの結果もコピペして聞いてみました。

すると、内容を細かく解説してくれたうえに、重要なポイント、つまり0と1の間違いを見つけてくれました。

ありがとう!i2cdetect の出力を確認したところ、重要なポイントがいくつか見つかりました

新たに修正が施されたコードは、先程のおまけと表示されていたコメントアウトはなくなっています。
代わりに# ← ここを 0 にするとご丁寧に指摘してありました。恐れ入ります。

import time
from smbus2 import SMBus

SHT30_ADDR = 0x44  # i2cdetect で見えていたアドレス

MEASUREMENT_CMD = [0x2C, 0x06]

def read_sht30(bus):
    # 測定コマンド送信
    bus.write_i2c_block_data(SHT30_ADDR, MEASUREMENT_CMD[0], [MEASUREMENT_CMD[1]])
    time.sleep(0.5)  # 測定待ち

    # データ読み取り
    data = bus.read_i2c_block_data(SHT30_ADDR, 0x00, 6)

    temp_raw = data[0] << 8 | data[1]
    temperature = -45 + (175 * temp_raw / 65535.0)

    humid_raw = data[3] << 8 | data[4]
    humidity = 100 * humid_raw / 65535.0

    return temperature, humidity

if __name__ == "__main__":
    with SMBus(0) as bus:  # ← ここを 0 にする
        while True:
            try:
                temperature, humidity = read_sht30(bus)
                print(f"温度: {temperature:.2f}°C, 湿度: {humidity:.2f}%")
            except Exception as e:
                print("エラー:", e)
            time.sleep(2)

senseor.pyを変更し、改めて実行してみます。
今度はエラーはなく実行できました。

出力結果:

sudo python3 sensor.py 

温度: 22.65°C, 湿度: 63.77%
温度: 22.65°C, 湿度: 63.81%
温度: 22.66°C, 湿度: 63.79%
温度: 22.64°C, 湿度: 63.80%
温度: 22.65°C, 湿度: 63.77%
温度: 22.64°C, 湿度: 63.78%

コンソール画面(ターミナル)に温度と湿度が繰り返し表示されました。いいですね。
終了するコードは書いてありませんから、Ctrl + C を押下して強制終了させます。

更にカスタマイズ(データの保存)

掲げた目標の通り、先ずは値を保存する機能を付けたいと思います。
一口に保存するといっても、テキストへ出力するだけもありますし、データベースに格納することも考えられます。

ただのテキスト出力より、データベースに格納して後からでも取り出せるようにした方が便利そうです。
データベースの種類も指定してあげると良いかもしれませんので、今回はファイル1つで済むSQLiteにしてみます。

意図した通り、次のような条件でデータベースに保存する機能が追加されたコードを出力してくれました。

  • 測定した「時刻」「温度」「湿度」を SQLite に記録
  • 起動時にテーブルがなければ自動作成
  • 2秒ごとに取得・保存・コンソールに出力

中身の精査はともかく、一度そのまま実行してみることにします。
新たにsensor_data.pyとして保存しました。

出力結果:

sudo python3 sensor_data.py 
[2025-04-11 12:37:54] 温度: 22.78°C, 湿度: 64.27%
[2025-04-11 12:37:57] 温度: 22.79°C, 湿度: 64.34%
[2025-04-11 12:37:59] 温度: 22.79°C, 湿度: 64.30%
[2025-04-11 12:38:02] 温度: 22.76°C, 湿度: 64.31%
[2025-04-11 12:38:04] 温度: 22.78°C, 湿度: 64.28%
[2025-04-11 12:38:07] 温度: 22.79°C, 湿度: 64.30%
[2025-04-11 12:38:09] 温度: 22.78°C, 湿度: 64.32%

出力結果の値は先程ど同様で、今度はタイムスタンプが付きました。

それでは、データベースにちゃんと保存されているか確認してみます。
「DB Browser for SQLite」というアプリがちょうど良いので、新たにインストールして覗いてみましょう。

sudo apt update
sudo apt install sqlitebrowser

sensor_data.dbというファイルが作成されているので読み込ませます。

Browse Dataタブで、ちゃんとデータが格納されているのが分かります。ここでは10件です。
これでいつでも値を取り出せるようになりました。

更にカスタマイズ(Webビュー)

次は、Webブラウザで表示できるように機能を追加します。
先程Flaskを使った提案がされていたので、そのままお願いしてみます。

Flaskはフレームワークと呼ばれるものです。先にインストールしておかないとなりません。
実は既にFlaskはインストールされていました。ただバージョンは1.0系で、そのまま2.0系や3.0系にアップグレードはできません。最新ではありませんが、今回はこのままで続けます。

インストールするならやはりpip3 installコマンドです。

flask --version

Python 3.9.2
Flask 1.1.2
Werkzeug 1.0.1

どうやら、新たにpyファイルを用意することになりました。

  • sensor.py → 温湿度取得&保存(前のデータ取得コード)
  • webapp.py → Flask アプリ

新たにwebapp.pyを作成します。

早速そのまま実行してみました。

python3 webapp.py 
 * Serving Flask app "webapp" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

ご覧のような文言がコンソールに表示されたら、Webブラウザにlocalhost:5000/と入力することで表示されます。
Raspberry PiのIPアドレスを指定すれば、別のマシンやスマホからも表示できます。(http://192.168.0.10:5000/など)

※簡易的なものなので、あくまでもローカル環境のテスト利用です。
このコードはエラーにはならず、修正の必要もありませんでした。

最後にグラフ表示を追加

最後にグラフ表示をさせれば、今回の目標にした機能が追加されたことになります。
plotlyを使ってみました。
plotlyはグラフ作成を可能にするオープンソース・ライブラリです。

先にplotlyをインストール:

pip3 install plotly

修正されたコードにwebapp.pyを変更します。

実行してみると、どういうわけかplotlyが読み込まれず表示されません。ヘッダタイトルだけであとは真っ白です。

一度、明示的にplotlyのcdnを指定すると良いとのことで、指定するパターンに入れ替えます。

render_template_string() で出力される HTML に <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>を手動で追加します。

これでもまだグラフが表示できません。
Webブラウザ側に出ていたエラーの結果をコピペして聞いてみた。

インターネットには繋がっているのですけど、明示的に指定したplotlyのcdnへ繋がらない状態です。

今度はローカルにjsファイルを指定する方法に変更することにしました。
ローカルにダウンロードしたjsファイルを指定するように書き換えます。

PCでダウンロード:https://cdn.plot.ly/plotly-latest.min.js

SCPコマンドでRaspberry Piへコピー:

scp plotly-latest.min.js pi@raspberrypi.local:/home/pi/yourapp/static/

新たに作成した/staticフォルダにplotly-latest.min.jsを移動します。

test/
├── webapp.py
├── sensor.py
├── sensor_data.py
└── static/
    └── plotly-latest.min.js

最後に、修正版webapp.pyのHTML箇所をローカルのパスに変更しました。

HTMLテンプレート内の変更箇所:

<script src="https://cdn.plot.ly/plotly-latest.min.js"></script> # 元のコード削除
  ↓
<script src="/static/plotly-latest.min.js"></script> # /staticフォルダのjsファイルを指定に変更

これで無事に表示できました。

グラフは、室内の安定した気温の中なので、取得するデータの値がほぼ変わりません。グラフとしてあまり意味はないです。
それでもグラフ表示の機能を持たせることができました。

ここから更に微修正しても良いですし、全く別の機能を試行錯誤してみるのも良いでしょう。
目標は達成できたので、今回はこれでテストを終了します。

本番環境で使うにはこれだけでは足りません。
これよりも高度なことや、エラー処理もあまりされていないため必要になります。

自身がプログラマーではないため、多くの機能を持たせたりすると少し無理があります。しかし、ホームユースはもちろん、テスト環境のコードとしては及第点ではないでしょうか。

質問力が要る

ChatGPTを使うのは、やはり質問力が必要です。大雑把に質問すると、重要な部分を省いて一般論になることが多くなり、的確な答えに感じられません。前提条件を一度並べた方が良いでしょう。

今回のように使用ハードウェアを産業用ラズベリーパイやComputeModule 4と限定してしまうと、これまた困る可能性が想像できたため、敢えて指定はしていません。環境が特殊だからです。

日本語文章になっていないエラー内容をそのままコピペしても改善点を指摘してくれます。
提案通りに実行しても、「何々ができない」と言葉で説明するだけでも適格な回答は含まれています。

最初の質問「SHT30とラズベリーパイを接続し、温度を計測するPythonプログラムを書いてください」から、5回くらいの質問で、概ね希望通りの機能を実装したPythonプログラムコードが出来上がりました。

正直、コードの中身を理解していなくても、指示通りでエラーが解消されることも多いですから、実際に使えるプログラムコードになり得ます。

コードの中で分からない部分を再質問することで、自身の理解も深まりますから、プログラミング学習の独学にも向いていますね。
インターネットで検索して調べるよりも、ピンポイントで調べられるといえば近い感覚でしょうか。

ケースバイケースにはなりますが、ChatGPTでのプログラミングは使えますね。

シェア機能

今回の記事での流れをChatGPTのシェア機能でリンクしておきます。
記事では省いている点も、設定で提案をオンにしているため細かく補足もありました。良ければ参考にしてください。

Raspberry Pi SHT30 温度計測
https://chatgpt.com/share/67f8a391-e8f0-8008-980d-f9a517819172

※特に有効期限はなくリンクさせています。但し、私が会話を削除してしまうとリンクが表示できなくなります。予めご了承ください。

参考リンク:
smbus2: https://pypi.org/project/smbus2/
plotly: https://github.com/plotly/plotly.js
DB Browser for SQLite: https://github.com/sqlitebrowser/sqlitebrowser
ChatGPT Shared Links FAQ: https://help.openai.com/en/articles/7925741-chatgpt-shared-links-faq


記事寄稿:ラズパイダ

非エンジニアでも楽しく扱えるRaspberry Pi 情報サイト raspida.com を運営。ラズベリーパイに長年触れた経験をもとに、ラズベリーパイを知る人にも、これから始めたいと興味を持つ人にも参考になる情報・トピックを数多く発信。PiLinkのサイトへは産業用ラズベリーパイについて技術ブログ記事を寄稿。