「屋外に設置したセンサーの電池が3日で切れてしまう…」「Wi-Fiが届かない場所にもデータを飛ばしたい…」そんな悩みを抱えている方、いませんか?

今回は、ESP32+MicroPythonを使って、LoRa通信MQTTを組み合わせた屋外向けIoTセンサーノードを自作する方法を解説します。さらに、電池を長持ちさせるためのDeepSleep(深睡眠)設計もしっかり組み込みます。

「LoRaもMQTTも聞いたことあるけど、両方同時に使うの?🤔」と思った方も大丈夫です。ざっくりとした役割分担から順番に説明していきますね。

🗺️ システム全体の構成をざっくり理解しよう

IoT outdoor sensor
IoT outdoor sensor / Photo by Ulrick Trappschuh via Pexels

まず、このシステムがどういう構成で動くのかを整理しておきましょう。

  • センサーノード(今回作るもの):ESP32+LoRaモジュール(RFM95/96などSX1276/SX1279系)+温湿度センサー(BME280など)
  • LoRaゲートウェイ:センサーノードからのLoRa信号を受信し、MQTTブローカーへ転送
  • MQTTブローカー:データを中継・管理するサーバー(Mosquittoなど)
  • クライアント:MQTTでサブスクライブしてデータを受け取るPC・スマホ・Dashboardなど

イメージとしては、LoRaが「遠距離の無線便」、MQTTが「データを仕分けする郵便局」という感じです。センサーノードは測定→送信→即眠る、というサイクルを繰り返して省電力を実現します。

🔧 必要なハードウェア

  • ESP32開発ボード(例:ESP32-DevKitC)
  • LoRaモジュール(RFM95/96などSX1276/SX1279系・920MHz帯技適取得済みのもの)
  • 温湿度・気圧センサー(BME280 ※I2C接続)
  • リチウムポリマーバッテリー(3.7V、2000mAh以上推奨)
  • ジャンパーワイヤー、ブレッドボード

配線のポイントをまとめるとこんな感じです👇

  • LoRaモジュールのSPI接続:SCK→GPIO18、MOSI→GPIO23、MISO→GPIO19、NSS→GPIO5、RST→GPIO14、DIO0→GPIO26
  • BME280のI2C接続:SDA→GPIO21、SCL→GPIO22

📦 MicroPythonの準備

ESP32へのMicroPythonファームウェア書き込みと、必要なライブラリの準備をしておきましょう。

  • MicroPythonファームウェア:公式サイト(micropython.org)からESP32用の最新版をダウンロード
  • 書き込みツール:esptool.py または Thonny IDE
  • 必要なライブラリ:sx127x.py(LoRaドライバ)、bme280.py(センサードライバ)、umqtt/simple.py(MQTTクライアント)

ライブラリはThonny IDEのファイルマネージャーからESP32の/libフォルダへ転送するのが手軽でおすすめです。

💻 メインコードを書いてみよう

それでは実際のコードを見ていきましょう。ここが今回の核心部分です!

# main.py — ESP32 LoRa+MQTT センサーノード(DeepSleep対応)
import time
import machine
import ubinascii
from machine import Pin, SPI, I2C, deepsleep
import bme280
from sx127x import SX127x

# ===== 設定値 =====
SLEEP_SEC = 60          # DeepSleep時間(秒)
LORA_FREQ = 920.9       # 周波数(日本の920MHz帯)
NODE_ID = ubinascii.hexlify(machine.unique_id()).decode()  # ノードID

# ===== BME280センサー初期化(I2C)=====
i2c = I2C(0, sda=Pin(21), scl=Pin(22), freq=400000)
bme = bme280.BME280(i2c=i2c)

def read_sensor():
    """温度・湿度・気圧を取得して辞書で返す"""
    temp, pressure, humidity = bme.read_compensated_data()
    return {
        "node": NODE_ID,
        "temp": round(temp / 100, 2),       # 単位: ℃
        "humidity": round(humidity / 1024, 2), # 単位: %
        "pressure": round(pressure / 25600, 2) # 単位: hPa
    }

# ===== LoRaモジュール初期化(SPI)=====
spi = SPI(1, baudrate=10000000,
          polarity=0, phase=0,
          sck=Pin(18), mosi=Pin(23), miso=Pin(19))

lora = SX127x(spi,
              pins={"ss": 5, "reset": 14, "dio_0": 26},
              frequency=LORA_FREQ)

def send_lora(data: dict):
    """辞書データをJSON文字列に変換してLoRa送信"""
    import ujson
    payload = ujson.dumps(data)
    print(f"[LoRa] 送信中: {payload}")
    lora.println(payload)
    time.sleep(0.5)  # 送信完了を待つ

# ===== メイン処理 =====
def main():
    print("[起動] センサーノード起動")

    # センサーデータ取得
    data = read_sensor()
    print(f"[センサー] {data}")

    # LoRa送信
    send_lora(data)

    # DeepSleepへ突入(指定秒後に自動起動)
    print(f"[Sleep] {SLEEP_SEC}秒間DeepSleepに入ります")
    deepsleep(SLEEP_SEC * 1000)  # ミリ秒単位

main()

ポイントをまとめるとこんな感じです👇

  • NODE_ID:ESP32固有のMACアドレスをIDとして使うことで、複数ノードを識別できます
  • DeepSleepdeepsleep()を呼ぶとESP32はほぼ電力ゼロの状態になり、時間が来ると自動でリセット起動します
  • LoRa送信後すぐ眠る:起きている時間を最短にすることが省電力の鍵です

📡 LoRaゲートウェイ側でMQTTに転送する


ゲートウェイ側(別のESP32やRaspberry Piなど)でLoRaパケットを受信し、MQTTブローカーへ転送するコードです。

# gateway.py — LoRa受信→MQTT転送(ゲートウェイ側)
import network
import time
from umqtt.simple import MQTTClient
from sx127x import SX127x
from machine import Pin, SPI

# ===== Wi-Fi接続 =====
SSID = "your_ssid"
PASSWORD = "your_password"
MQTT_BROKER = "192.168.1.100"  # ブローカーのIPアドレス
MQTT_TOPIC = b"sensor/outdoor"

def connect_wifi():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(SSID, PASSWORD)
    while not wlan.isconnected():
        time.sleep(0.5)
    print(f"[Wi-Fi] 接続完了: {wlan.ifconfig()[0]}")

# ===== MQTT接続 =====
def connect_mqtt():
    client = MQTTClient("lora_gateway", MQTT_BROKER)
    client.connect()
    print("[MQTT] ブローカー接続完了")
    return client

# ===== LoRa受信ループ =====
def receive_loop(lora, mqtt_client):
    print("[ゲートウェイ] LoRa受信待機中...")
    while True:
        if lora.received_packet():             # パケット受信チェック
            payload = lora.read_payload()      # データ取得
            rssi = lora.packet_rssi()          # 信号強度取得
            print(f"[受信] RSSI={rssi}dBm payload={payload}")

            # MQTTに転送(バイト列で送信)
            mqtt_client.publish(MQTT_TOPIC, payload)
            print("[MQTT] 転送完了")

        time.sleep(0.1)

# ===== 起動 =====
connect_wifi()
client = connect_mqtt()

spi = SPI(1, baudrate=10000000,
          polarity=0, phase=0,
          sck=Pin(18), mosi=Pin(23), miso=Pin(19))
lora = SX127x(spi,
              pins={"ss": 5, "reset": 14, "dio_0": 26},
              frequency=920.9)

receive_loop(lora, client)

ゲートウェイはWi-Fiに常時接続するため、こちらはDeepSleepなし・常時起動のスタイルにしています。

🔋 DeepSleep省電力設計のポイント

屋外で長期運用するなら、消費電力の設計が本当に重要です。主なポイントをまとめます。

  • 起動時間を最短に:Wi-Fi不使用 → Wi-Fi接続のオーバーヘッドがゼロ。LoRaなら送信まで1〜2秒で完了
  • DeepSleep中の消費電流:ESP32のDeepSleepはおよそ10〜20μA。通常動作時(Wi-Fi ON)の240mAと比べると桁違いに省電力です
  • 測定間隔の調整:SLEEP_SECを60(1分)→600(10分)にするだけでバッテリー寿命が約10倍に延びます
  • センサーへの給電制御:BME280の電源ピンをGPIOで制御し、Sleep中は電源をOFFにするとさらに節電できます

2000mAhのバッテリーを使った場合の理論稼働時間の目安はこちらです👇

  • 測定間隔1分:約30〜45日
  • 測定間隔10分:約150〜180日
  • 測定間隔30分:約300日以上(ソーラー補助なしでも1年近く)

📊 MQTTデータをPC側で受け取って確認する

PC(Python環境)でMQTTを購読してデータを確認するシンプルなコードも書いておきましょう。

# subscriber.py — PCでMQTTデータを受信して表示
import paho.mqtt.client as mqtt
import json

BROKER = "192.168.1.100"
TOPIC = "sensor/outdoor"

def on_message(client, userdata, msg):
    """メッセージ受信時のコールバック"""
    try:
        data = json.loads(msg.payload.decode())
        print(f"[受信] ノード: {data['node']}")
        print(f"  温度: {data['temp']}℃")
        print(f"  湿度: {data['humidity']}%")
        print(f"  気圧: {data['pressure']}hPa")
        print("-" * 30)
    except Exception as e:
        print(f"[エラー] パース失敗: {e}")

client = mqtt.Client()
client.on_message = on_message
client.connect(BROKER, 1883)
client.subscribe(TOPIC)

print(f"[購読開始] topic={TOPIC}")
client.loop_forever()  # 永続的に受信待機

このスクリプトを実行しておくと、センサーノードが起きるたびにリアルタイムでデータが表示されます。

🌿 屋外運用で気をつけること


  • 防水・防塵対策:IP65以上のケースにESP32ごと収納し、センサー部分だけ外気に触れるよう設計する
  • LoRaアンテナ:アンテナが正しく接続されていないと通信距離が激減します。必ず取り付け確認を
  • 日本の電波法:920MHz帯(920.6〜928.0MHz)の技適取得済みモジュールを使用してください
  • 温度範囲:ESP32の動作温度は-40〜85℃ですが、リチウム電池は低温で容量が落ちます。冬場は注意が必要です

まとめ

今回はESP32+MicroPythonでLoRaとMQTTを組み合わせた屋外IoTセンサーノードの作り方を解説しました。

  • LoRaでWi-Fiの届かない場所でも長距離通信が可能
  • MQTTでゲートウェイからクラウドまでシームレスにデータ転送
  • DeepSleepで単三電池運用でも数ヶ月稼働できる省電力設計

「Wi-Fiが届かない畑や駐車場にもセンサーを置きたい!」という方にとって、このLoRa+MQTT構成はベストな選択肢のひとつです。まずは室内でLoRa通信が成功するところから確認してみてください。

ぜひ自分だけの屋外センサーノードを作って、長期データ収集にチャレンジしてみてくださいね😊

📡 Arduinoをもっと深く学ぼう!

Arduino・ラズパイ・ロボットプログラミングを体系的に学びたい方へ。おすすめのUdemyコースや電子部品もまとめています。

Arduinoロボット入門のおすすめコース・部品を見る →

📚 関連商品・おすすめ書籍

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

もしも

スッキリわかるPython入門 第2版 (スッキリわかる入門シリーズ)

初心者に定番のPython入門書

Amazonで見る

Arduinoをはじめよう 第4版 (Make: PROJECTS)

もしも

Arduinoをはじめよう 第4版 (Make: PROJECTS)

Arduino公式推薦の定番入門書

Amazonで見る

徹底攻略! 電子工作&プログラミング Arduinoで学ぶ電子工作完全ガイド

もしも

徹底攻略! 電子工作&プログラミング Arduinoで学ぶ電子工作完全ガイド

電子工作とプログラミングを同時に学べる

Amazonで見る

※本記事にはアフィリエイトリンクが含まれます。

ABOUT ME
やまちゃん
これまで学生と社会人を合わせて5000人以上にプログラミング学習を指導。 ゼロからイチをわかりやすく解説する専門家として活動しており、本業ではArduinoを用いたIoT開発とロボットプログラミングが専門。 Pythonを用いたアプリ開発、ウェブアプリケーションの開発で業務の効率化をサポートしています。