PR

Raspberry Pi Pico Wで作る熱中症警戒アラート表示器

ハードウェア制御 / 電子工作

DIY LAB VOL.1

スポンサーリンク

Raspberry Pi Pico Wで作る熱中症警戒アラート表示器

こんにちは。ニンジンです🥕

今回は「物作り系」の新シリーズとして、Raspberry Pi Pico Wと温湿度センサーDHT11、0.96インチOLEDディスプレイを使って、熱中症警戒アラート表示器を自作してみました。気温・湿度から簡易的な暑さ指数を計算し、「ほぼ安全・注意・警戒・厳重警戒・危険」の5段階を、OLED上の顔アイコンで直感的に表示します。

夏休みの自由研究のテーマとしても、Raspberry Pi Picoで「センサーの値を取得する→計算する→表示する」という電子工作の基本の流れを一通り体験できる題材になっています。実際に指でセンサーに触れて警戒レベルが切り替わる様子を動画にまとめたので、まずはご覧ください。

この記事で分かること

  • DHT11センサーで温度・湿度を取得する方法
  • 0.96インチOLED(SSD1306)にMicroPythonで文字・図形を表示する方法
  • 気温・湿度から簡易的な暑さ指数(WBGT)を計算し、5段階で警戒レベルを判定する方法
  • framebufのpixel・line・fill_rectを組み合わせて顔アイコンを描く方法
  • 作ったプログラムをPico本体に保存し、PCなしで単体動作させる方法
  • 実際に制作中つまずいたポイント(COMポートエラー、日本語アカウント名でのライブラリインストール失敗、ブレイクアウトボードの半挿入など)

DIY LAB VOL.1

スポンサーリンク

今回使用する部品一覧(Raspberry Pi Pico W・DHT11・OLED SSD1306)

今回使用した部品は以下の6点です。ブレッドボード上で組み立てるため、ハンダ付けは一切不要です。

今回使用する部品一式
▲ 今回使用する部品一式
Raspberry Pi Pico WH(ヘッダーピン実装済み)
必須

コード書き込みまで一直線、ヘッダー付きだからハンダ付け不要

Raspberry Pi Pico WH(ヘッダーピン実装済み)

無線LAN対応のマイコンボード。あらかじめピンヘッダーが取り付け済みのWHモデルなら、届いてすぐブレッドボードに挿して配線を始められます。

無線LAN対応ピンヘッダー実装済みMicroPython対応
Freenove Raspberry Pi Pico用ブレイクアウトボード
必須

Picoを挿すだけでピン配置が一覧できる縁の下の力持ち

Freenove Raspberry Pi Pico用ブレイクアウトボード

Pico(W/WH)を差し込むだけで全ピンを分かりやすくラベル表示してくれる拡張ボード。配線ミスの防止に地味に効きます。

ピン名表示半田付け不要
DHT11 温湿度センサーモジュール
必須

気温・湿度をこれ1個でまとめて取得

DHT11 温湿度センサーモジュール

温度・湿度を同時に計測できる定番センサー。デジタル1本の信号線で読み取れるので配線もシンプルです。

温湿度センサーデジタル出力
0.96インチ OLEDディスプレイ(I2C接続・SSD1306)
必須

警戒レベルを顔アイコンで表示する主役ディスプレイ

0.96インチ OLEDディスプレイ(I2C接続・SSD1306)

I2C接続の128×64モノクロ有機ELディスプレイ。配線がSDA/SCLの2本だけで済み、消費電力も少ないのが特徴です。

I2C接続SSD1306
ブレッドボード 400穴
必須

ハンダ付けなしで試作できる電子工作の土台

ブレッドボード 400穴

DHT11・OLED・Picoをまとめて配線するための土台。400穴サイズなら今回の構成には十分な広さです。

ハンダ付け不要400ポイント
ELEGOO 50pcsオスメスジャンパーワイヤ(200mm)
必須

配線に足りないワイヤーはこれで解決

ELEGOO 50pcsオスメスジャンパーワイヤ(200mm)

オスメスのジャンパーワイヤ50本セット。DHT11・OLEDのようなモジュール接続に向いています。付属の170タイポイントのミニブレッドボードもおまけで使えます。

オスメス200mmミニブレッドボード付属

DIY LAB VOL.1

スポンサーリンク

ハードウェアの配線:Pico WとDHT11・OLED(I2C)の結線図

配線は下図の通りです。DHT11とOLEDで3.3V・GNDのラインを共用しているので、ブレッドボードの電源レール(赤帯・青帯)にまとめて配っています。

Pico W・DHT11・OLEDの結線図
▲ Pico W・DHT11・OLEDの結線図

DHT11の配線

DHT11役割Pico Wピン
VCC電源36番(3V3(OUT))
GNDグランド38番(GND)
DATAデータ21番(GP16)

0.96インチOLED(I2C接続)の配線

OLED役割Pico Wピン
VCC電源36番(3V3(OUT))※DHT11と共用
GNDグランド38番(GND)※DHT11と共用
SDAI2Cデータ1番(GP0)
SCLI2Cクロック2番(GP1)
実際に配線した様子
▲ 実際に配線した様子
ポイント:ブレッドボードの電源ライン(赤帯・青帯)は、機種によって真ん中で左右に分断されているものがあります。電源が届かない場合は、まずこの分断がないか確認してください。

DIY LAB VOL.1

スポンサーリンク

開発環境の構築:ThonnyとMicroPythonの書き込み

Pico WにMicroPythonを書き込み、開発ソフト「Thonny」をセットアップする手順は、以前の記事で詳しく解説しています。今回もこの手順に沿ってセットアップしました。

Thonnyのインストール〜MicroPython書き込み手順はこちら

記事を読む
つまずきポイント:Thonnyで「Unable to connect to COM6: port not found」というエラーが出て接続できないことがあります。多くの場合、Thonny側のポート設定が以前接続した時のCOM番号に固定されたままになっているのが原因です。ポート設定を「Try to detect port automatically」(自動検出)に変更するか、デバイスマネージャーで実際のCOM番号を確認して選び直してください。

DIY LAB VOL.1

スポンサーリンク

DHT11とOLED(SSD1306)の単体動作確認

配線ができたら、DHT11とOLEDがそれぞれ単体で正しく動くか確認します。

micropython-ssd1306ライブラリの導入

MicroPython標準にはSSD1306用のドライバが含まれていないため、Thonnyの「ツール」→「パッケージの管理」から追加します。

ツール→パッケージの管理を選択
▲ ツール→パッケージの管理を選択
micropython-ssd1306を検索
▲ micropython-ssd1306を検索
インストールボタンをクリック
▲ インストールボタンをクリック
インストールが進む様子
▲ インストールが進む様子
つまずきポイント:Windowsのユーザーアカウント名が日本語表記になっていると、このインストールがエラーになることがあります。ログインアカウント名を半角英数字にしたところ、インストールに成功しました。pip系のインストーラーは、ユーザーフォルダのパスに日本語(全角文字)が含まれていると失敗することがあるため、心当たりがある方は確認してみてください。

OLEDの表示テスト

from machine import Pin, I2C
import ssd1306

# I2C接続確認(配線ミス時はここで空リストが出ます)
i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=400000)
print("I2Cデバイス検出:", i2c.scan())  # 通常 [60] (0x3C) が出れば正常

oled = ssd1306.SSD1306_I2C(128, 64, i2c)
oled.fill(0)
oled.text("Hello, Pico W!", 0, 0)
oled.text("OLED Test OK", 0, 20)
oled.show()
Thonny側の実行結果
▲ Thonny側の実行結果(I2Cデバイス検出:[60])
OLEDにHello Pico Wと表示された様子
▲ OLEDに「Hello, Pico W!」と表示された様子

DHT11のシリアル出力テスト

from machine import Pin
import dht
import time

sensor = dht.DHT11(Pin(16))

while True:
    try:
        sensor.measure()
        temp = sensor.temperature()
        hum = sensor.humidity()
        print("温度: {}C  湿度: {}%".format(temp, hum))
    except OSError as e:
        print("センサー読み取りエラー:", e)
    time.sleep(2)
Thonny側の実行結果、温湿度がコンソールに表示
▲ Thonny側の実行結果(温湿度がコンソールに表示)
つまずきポイント:OLED・DHT11の両方が同時にOSError: [Errno 110] ETIMEDOUTで失敗したことがありました。この2つは使用するピンが全く別なので、個別の配線ミスというより電源(3.3V/GND)まわりの共通トラブルを疑うのがポイントです。今回の原因は、FreenoveブレイクアウトボードとPico本体の挿し込みが甘い「半挿入」でした。見た目では気づきにくいので、複数のデバイスが同時にタイムアウトする場合は、まず全体の挿し込みを奥まで確認し直してみてください。

DIY LAB VOL.1

スポンサーリンク

気温・湿度から熱中症警戒レベルを計算する(簡易WBGT推定式)

DHT11は気温と湿度しか計測できず、正式な暑さ指数(WBGT)の算出に必要な黒球温度(輻射熱)は測れません。そのため今回は、気温・湿度から屋内向けに簡易推定する計算式を使っています。環境省が発表する正式な熱中症警戒アラートとは異なる簡易的な目安である点にご注意ください。

まずTetensの式で水蒸気圧を求め、それを使って屋内簡易WBGT推定式を計算します。

def estimate_wbgt(temp, humidity):
    # Tetensの式で水蒸気圧(hPa)を算出
    e = (humidity / 100) * 6.1078 * 10 ** (7.5 * temp / (temp + 237.3))
    # 屋内(日射なし)簡易WBGT推定式
    return 0.567 * temp + 0.393 * e + 3.94

算出したWBGT推定値を、日本生気象学会の熱中症予防運動指針を参考にした5段階のしきい値で判定します。

WBGT推定値警戒レベル
21未満ほぼ安全
21以上25未満注意
25以上28未満警戒
28以上31未満厳重警戒
31以上危険
def classify_wbgt(wbgt):
    if wbgt < 21:
        return "ほぼ安全"
    elif wbgt < 25:
        return "注意"
    elif wbgt < 28:
        return "警戒"
    elif wbgt < 31:
        return "厳重警戒"
    else:
        return "危険"

実際に室温26℃・湿度49%の環境で試したところ、推定WBGTは25.2、警戒レベルは「警戒」と表示されました。手計算でも同じ値になることを確認済みです。

気温26度湿度49%で推定WBGT25.2警戒と表示された実行結果
▲ 気温26℃・湿度49%で推定WBGT25.2「警戒」と表示された実行結果

DIY LAB VOL.1

スポンサーリンク

OLEDに顔アイコンで警戒レベルを表示する

数値だけでなく、一目で危険度が伝わるよう、OLEDに5段階の表情を顔アイコンで表示します。MicroPython標準のフォントは日本語(漢字)に対応していないため、レベル名は英語表記(SAFE/CAUTION/WARNING/SEVERE/DANGER)にしています。

顔のパーツはframebufの基本図形だけで表現しています。目はfill_rectで塗りつぶした四角、口は2次曲線の計算式で1ピクセルずつpixelを打って描き、mood(表情の度合い)の符号でにっこり顔としかめ顔を切り替えています。厳重警戒・危険レベルでは、ハの字の眉と汗マークも追加しました。

def draw_face(oled, mood):
    # mood: 2=ほぼ安全(にっこり) 〜 -2=危険(困り顔)
    cx, cy = 64, 34
    eye_y = cy - 8
    # 目
    oled.fill_rect(cx - 14, eye_y, 4, 4, 1)
    oled.fill_rect(cx + 10, eye_y, 4, 4, 1)
    # 眉(厳重警戒・危険のみハの字に)
    if mood <= -1:
        oled.line(cx - 17, eye_y - 4, cx - 10, eye_y - 1, 1)
        oled.line(cx + 17, eye_y - 4, cx + 10, eye_y - 1, 1)
    # 口(2次曲線。mood>0で笑顔、mood<0でしかめ顔)
    mouth_w = 20
    mouth_y = cy + 10
    amplitude = 6
    for dx in range(-mouth_w, mouth_w + 1):
        dy = mood * amplitude * (1 - (dx / mouth_w) ** 2)
        oled.pixel(cx + dx, int(mouth_y + dy), 1)
    # 危険レベルは汗マーク追加
    if mood <= -2:
        oled.line(cx + 22, eye_y - 6, cx + 20, eye_y + 2, 1)
        oled.line(cx + 20, eye_y + 2, cx + 24, eye_y + 2, 1)

5段階それぞれの表示結果です。

SAFEほぼ安全
▲ SAFE(ほぼ安全)
CAUTION注意
▲ CAUTION(注意)
WARNING警戒
▲ WARNING(警戒)
SEVERE厳重警戒
▲ SEVERE(厳重警戒)
DANGER危険、ハの字眉と汗マークが追加される
▲ DANGER(危険)。ハの字眉と汗マークが追加される

DHT11・OLED・計算ロジック・顔アイコン表示をすべて統合した本番用スクリプトは以下の通りです。

from machine import Pin, I2C
import dht
import ssd1306
import time

# I2C・DHT11初期化
i2c = I2C(0, scl=Pin(1), sda=Pin(0), freq=400000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
sensor = dht.DHT11(Pin(16))

# しきい値(WBGT上限, mood, 表示ラベル)
LEVELS = [
    (21,  2,  "SAFE"),
    (25,  1,  "CAUTION"),
    (28,  0,  "WARNING"),
    (31, -1,  "SEVERE"),
    (999, -2, "DANGER"),
]

def estimate_wbgt(temp, humidity):
    e = (humidity / 100) * 6.1078 * 10 ** (7.5 * temp / (temp + 237.3))
    return 0.567 * temp + 0.393 * e + 3.94

def classify(wbgt):
    for threshold, mood, label in LEVELS:
        if wbgt < threshold:
            return mood, label
    return LEVELS[-1][1], LEVELS[-1][2]

def draw_face(mood):
    cx, cy = 64, 34
    eye_y = cy - 8
    oled.fill_rect(cx - 14, eye_y, 4, 4, 1)
    oled.fill_rect(cx + 10, eye_y, 4, 4, 1)
    if mood <= -1:
        oled.line(cx - 17, eye_y - 4, cx - 10, eye_y - 1, 1)
        oled.line(cx + 17, eye_y - 4, cx + 10, eye_y - 1, 1)
    mouth_w = 20
    mouth_y = cy + 10
    amplitude = 6
    for dx in range(-mouth_w, mouth_w + 1):
        dy = mood * amplitude * (1 - (dx / mouth_w) ** 2)
        oled.pixel(cx + dx, int(mouth_y + dy), 1)
    if mood <= -2:
        oled.line(cx + 22, eye_y - 6, cx + 20, eye_y + 2, 1)
        oled.line(cx + 20, eye_y + 2, cx + 24, eye_y + 2, 1)

def draw_status(temp, hum, label, mood):
    oled.fill(0)
    oled.text("T:{}C H:{}%".format(temp, hum), 0, 0)
    draw_face(mood)
    oled.text(label, 40, 56)
    oled.show()

while True:
    try:
        sensor.measure()
        temp = sensor.temperature()
        hum = sensor.humidity()
        wbgt = estimate_wbgt(temp, hum)
        mood, label = classify(wbgt)
        print("気温:{}C 湿度:{}% 推定WBGT:{:.1f} レベル:{}".format(temp, hum, wbgt, label))
        draw_status(temp, hum, label, mood)
    except OSError as e:
        print("センサー読み取りエラー:", e)
    time.sleep(2)

実際にDHT11に指で触れて温度・湿度を上げると、WARNING→SEVEREへとレベルが切り替わる様子が確認できました。

シェル出力、湿度上昇に伴いWARNINGからSEVEREへ変化
▲ シェル出力。湿度上昇に伴いWARNING→SEVEREへ変化
OLED実機でのWARNING表示
▲ OLED実機でのWARNING表示
OLED実機でのSEVERE表示
▲ OLED実機でのSEVERE表示

DIY LAB VOL.1

スポンサーリンク

main.pyをPico本体に保存してスタンドアロン動作させる

MicroPythonは電源投入時にboot.py→main.pyの順で自動実行する仕組みがあります。Pico本体のストレージにmain.pyという名前で保存しておけば、PCもThonnyも繋がずモバイルバッテリーなどの電源だけで単体動作します。

つまずきポイント:Thonnyでコードを実行・保存したつもりが、実はPC側(デスクトップなど)にファイルを保存しただけで、Pico本体には保存されていなかったことがありました。この状態だとPCから切り離すと動かなくなります。保存先は必ず「Raspberry Pi Pico」を選び、ファイル名はmain.pyのままにしてください。
ファイル→名前を付けて保存で保存先を選ぶダイアログ、Raspberry Pi Picoを選択
▲ 「ファイル」→「名前を付けて保存」で保存先を選ぶダイアログ。「Raspberry Pi Pico」を選択
ファイル名をmain.pyのままOK
▲ ファイル名をmain.pyのまま「OK」

保存後、USBケーブルを一度抜き差しして、PCから切り離した状態でも動作することを確認しました。


DIY LAB VOL.1

スポンサーリンク

完成品と動作の様子

完成した熱中症警戒アラート表示器の全体像です。

完成した装置の全体俯瞰
▲ 完成した装置の全体俯瞰

指でDHT11に触れて温度・湿度を上げ、警戒レベルが切り替わっていく様子を動画にまとめました(記事冒頭のものと同じ動画です)。


DIY LAB VOL.1

スポンサーリンク

まとめ・次回予告

Raspberry Pi Pico WとDHT11、0.96インチOLED(SSD1306)を使って、熱中症警戒アラート表示器を自作しました。センサーの値を取得し、計算し、表示するという電子工作の基本の流れを、実際に手を動かしながら一通り体験できたと思います。

今回作った回路と考え方は、DHT11以外のセンサーやOLED以外のディスプレイにも応用が効きます。夏休みの自由研究や、ちょっとした電子工作の入り口として、ぜひ挑戦してみてください。

次回予告

次回は、この装置をベースにした発展編を予定しています。お楽しみに。


DIY LAB VOL.1

スポンサーリンク

よくある質問

QDHT11で計算した警戒レベルは、環境省が発表する熱中症警戒アラートと同じですか?

Aいいえ、正式な暑さ指数(WBGT)ではありません。黒球温度(輻射熱)を測定していない、気温・湿度のみからの簡易推定値です。目安としてご利用ください。

QOLEDに「警戒」などの日本語をそのまま表示できますか?

AMicroPython標準のフォントはASCIIのみ対応のため、漢字はそのままでは表示できません。本記事ではSAFE/CAUTION/WARNING/SEVERE/DANGERの英語表記を使っています。

Qプログラムを保存したのに、PCを外すと動かなくなります。

AThonnyでファイルを保存する際、保存先が「このコンピュータ」になっている可能性があります。「Raspberry Pi Pico」を選び、ファイル名をmain.pyにして保存し直してください。

QOLEDが「OSError: [Errno 110] ETIMEDOUT」で動きません。

AI2C配線の接続不良が主な原因です。特にブレイクアウトボードとPico本体の挿し込みが甘い「半挿入」で発生しやすいので、奥までしっかり挿し直してみてください。

QDHT11のDATAピンは何番を使えばいいですか?

A本記事ではGP16(物理21番ピン)を使用していますが、他の空きGPIOでも動作します。使用するピンに合わせてコード内のPin(16)を書き換えてください。

Qmicropython-ssd1306のインストールでエラーが出ます。

AWindowsのユーザーアカウント名が日本語表記の場合、インストールが失敗することがあります。アカウント名を半角英数字に変更すると解決する場合があります。


ニンジン🥕アイコン

Follow me on X

本ブログの中の人「ニンジン🥕」はXやってます。新着記事はいち早くポストでお知らせしているので、見逃したくない方は @ninjin_py_vba をフォローしてみてください🥕

@ninjin_py_vba をフォロー

この記事が役に立ったら、シェアしてもらえると嬉しいです🥕

Xでポスト はてブ LINE Pocket

PROJECT LOG — SERIES INDEX
スポンサーリンク

シリーズ記事一覧

電子工作・自動化ツールの製作記録をシリーズごとにまとめています。気になるテーマからお読みください。


Tapo P110Mの消費電力をExcelレポート化する【年次集計・電気代試算・セキュリティ判断編】
FastAPI+openpyxlでTapo P110Mの消費電力をExcelレポート化。15秒間隔の生ログを日次集計する設計や、31円/kWhでの電気代試算、外部公開を見送った理由まで解説します。
Tapo P110M × FastAPIで電力監視ダッシュボードを作る【SQLite記録・リアルタイム堅牢化編】
Tapo P110MのデータをSQLiteに記録し、FastAPI+Uvicornでリアルタイム電力監視ダッシュボードを作る方法を解説。通信失敗時の自動復旧など堅牢化のポイントも紹介します。
Tapo P110M × python-kasaで自作PDUを作る【環境構築・Unsupported device(TPAP)エラー解決編】
Tapo P110Mをpython-kasaで自作PDU化。「Unsupported device」TPAPエラーの原因と対処法、MACアドレスでIP変動に強くする設計、個別/一括ON-OFFの実装コードまで解説します。
Raspberry Pi Pico Wで作る熱中症警戒アラート表示器
Raspberry Pi Pico WとDHT11、0.96インチOLEDで熱中症警戒アラート表示器を自作。気温・湿度から簡易的な暑さ指数を計算し、5段階の顔アイコンで危険度を表示する作り方をコード付きで解説します。
CustomTkinterでオリジナルのメニューバー(File・表示・設定・Help)を自作する
CustomTkinterには無いメニューバー(File・表示・設定・Help)を、CTkFrameとCTkButtonで自作する方法を、5段階のサンプルコードとダウンロード付きで解説します。

コメント