ESP32+MicroPythonでLoRa×MQTTを使った屋外IoTセンサーノードを自作する方法【DeepSleep省電力設計】
「屋外に設置したセンサーの電池が3日で切れてしまう…」「Wi-Fiが届かない場所にもデータを飛ばしたい…」そんな悩みを抱えている方、いませんか?
今回は、ESP32+MicroPythonを使って、LoRa通信とMQTTを組み合わせた屋外向けIoTセンサーノードを自作する方法を解説します。さらに、電池を長持ちさせるためのDeepSleep(深睡眠)設計もしっかり組み込みます。
「LoRaもMQTTも聞いたことあるけど、両方同時に使うの?🤔」と思った方も大丈夫です。ざっくりとした役割分担から順番に説明していきますね。
🗺️ システム全体の構成をざっくり理解しよう

まず、このシステムがどういう構成で動くのかを整理しておきましょう。
- センサーノード(今回作るもの):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として使うことで、複数ノードを識別できます
- DeepSleep:
deepsleep()を呼ぶと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コースや電子部品もまとめています。
📚 関連商品・おすすめ書籍
※本記事にはアフィリエイトリンクが含まれます。





