
MQTTで双方向通信!Oracleのデータを子機に配信する
HowTo16分で読めます
このシリーズ: 全5回
はじめに
前回までの記事では、子機→親機→Oracle という一方通行のデータの流れを作りました。
でも、こんな疑問が浮かびませんか?
「逆に、Oracleのデータを子機に送ることはできないの?」 「そもそも、Raspberry Piにデータベースを入れちゃダメなの?」
この記事では、MQTTの双方向通信と、 ローカルDBとMQTTの違いについて、わかりやすく解説します。
双方向通信とは?
今までの構成(一方通行)
text
┌─────────────────────────────────────────────────────────────────┐
│ 一方通行(Upload Only) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 子機(カメラ) │
│ ┌─────────────┐ │
│ │ 📷 設備状態 │ │
│ │ を検知 │ │
│ └──────┬──────┘ │
│ │ │
│ │ Publish(送信のみ) │
│ ▼ │
│ ┌─────────────┐ │
│ │ 中継機 │ │
│ │ Mosquitto │ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ ☁️ Oracle │ │
│ │ データ蓄積 │ │
│ └─────────────┘ │
│ │
│ ❌ 子機はOracleのデータを見れない │
│ ❌ 設定変更するには子機に直接アクセスが必要 │
│ │
└─────────────────────────────────────────────────────────────────┘双方向通信の構成
text
┌─────────────────────────────────────────────────────────────────┐
│ 双方向通信(Bidirectional) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 子機(カメラ) │
│ ┌─────────────┐ │
│ │ 📷 設備状態 │ │
│ │ を検知 │ │
│ └──────┬──────┘ │
│ │ ▲ │
│ │ │ ↑ Subscribe(受信も!) │
│ │ │ ・設定変更の指示 │
│ │ │ ・他の設備の状態 │
│ │ │ ・マスターデータ │
│ ▼ │ │
│ ┌─────────────┐ │
│ │ 中継機 │ │
│ │ Mosquitto │ │
│ └──────┬──────┘ │
│ │ ▲ │
│ ▼ │ │
│ ┌─────────────┐ │
│ │ ☁️ Oracle │ │
│ │ データ蓄積 │ │
│ └─────────────┘ │
│ │
│ ✅ 子機もOracleのデータを受け取れる │
│ ✅ 遠隔から子機の設定を変更できる │
│ │
└─────────────────────────────────────────────────────────────────┘双方向通信の使い道
ユースケース1: 遠隔設定変更
text
┌─────────────────────────────────────────────────────────────────┐
│ 子機の設定を遠隔で変更したい │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 【従来】直接子機にSSHでログインして設定変更 │
│ │
│ 管理者PC 子機 │
│ ┌─────────┐ ┌─────────┐ │
│ │ 💻 │ ──── SSH ────────→ │ 📷 │ │
│ └─────────┘ └─────────┘ │
│ │
│ 問題点: │
│ ・子機が100台あったら100回ログイン? │
│ ・ネットワーク障害で接続できないことも │
│ ・設定ミスで子機が動かなくなるリスク │
│ │
│ ───────────────────────────────────────────────────── │
│ │
│ 【MQTT】設定変更をMQTTで配信 │
│ │
│ 管理者PC Broker 子機① 子機② 子機③ │
│ ┌─────────┐ ┌─────────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │ 💻 │ ─→ │Mosquitto│ ─→│ 📷 │ │ 📷 │ │ 📷 │ │
│ └─────────┘ └─────────┘ └─────┘ └─────┘ └─────┘ │
│ │ │
│ └→ Publish: config/all {"interval": 5} │
│ → 全子機が同時に設定変更! │
│ │
└─────────────────────────────────────────────────────────────────┘ユースケース2: マスターデータの配信
text
┌─────────────────────────────────────────────────────────────────┐
│ 設備名などのマスターデータを配信 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Oracle DB │
│ ┌─────────────────────────────────────────┐ │
│ │ EQUIPMENT_MASTER テーブル │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ EQ_CODE | EQ_NAME | LOCATION │ │ │
│ │ │ ─────────────────────────────── │ │ │
│ │ │ EQ001 | プレス機A | 工場1F │ │ │
│ │ │ EQ002 | 溶接ロボB | 工場2F │ │ │
│ │ └─────────────────────────────────┘ │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ │ ブリッジがOracleから取得 │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Broker (Mosquitto) │ │
│ │ │ │
│ │ Publish: master/equipment │ │
│ │ {"EQ001": "プレス機A", "EQ002": ...} │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ┌───────────┼───────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 子機① │ │ 子機② │ │ 子機③ │ │
│ │ EQ001の │ │ EQ002の │ │ EQ003の │ │
│ │ 名前を取得 │ │ 名前を取得 │ │ 名前を取得 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ → 子機のUIに設備名を表示できる! │
│ │
└─────────────────────────────────────────────────────────────────┘ユースケース3: 他の設備の状態を取得
text
┌─────────────────────────────────────────────────────────────────┐
│ 子機が他の設備の状態を知りたい場合 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 例: EQ001の子機が、EQ002の状態によって動作を変えたい │
│ │
│ 子機① (EQ001) 子機② (EQ002) │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 📷 カメラ │ │ 📷 カメラ │ │
│ │ │ │ │ │
│ │ Subscribe: │ │ Publish: │ │
│ │ equipment/ │ ←──────────→ │ equipment/ │ │
│ │ status/EQ002│ Broker経由 │ status/EQ002│ │
│ └─────────────┘ └─────────────┘ │
│ │
│ 子機①は、子機②の状態を直接知ることができる! │
│ │
│ 使用例: │
│ ・前工程が停止したら、自分も省エネモードに │
│ ・異常を検知したら、関連設備に警告を送る │
│ │
└─────────────────────────────────────────────────────────────────┘双方向通信の実装方法
トピック設計
text
┌─────────────────────────────────────────────────────────────────┐
│ トピックの設計例 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 【上り(子機→Oracle)】 │
│ equipment/status/{eq_code} 設備状態の送信 │
│ equipment/alert/{eq_code} アラートの送信 │
│ │
│ 【下り(Oracle→子機)】 │
│ config/all 全子機への設定配信 │
│ config/{eq_code} 特定子機への設定配信 │
│ master/equipment 設備マスターの配信 │
│ command/{eq_code} 特定子機へのコマンド │
│ │
│ 【横(子機↔子機)】 │
│ equipment/status/# 他の設備の状態を購読 │
│ │
└─────────────────────────────────────────────────────────────────┘子機側のコード例
python
# 子機で送受信両方を行う例
import paho.mqtt.client as mqtt
import json
BROKER = "192.168.32.213"
MY_EQUIPMENT = "EQ001"
# 受信時のコールバック
def on_message(client, userdata, msg):
topic = msg.topic
data = json.loads(msg.payload)
if topic == "config/all":
# 全体設定を受信
print(f"設定変更: {data}")
apply_config(data)
elif topic == f"config/{MY_EQUIPMENT}":
# 自分宛の設定を受信
print(f"個別設定: {data}")
apply_config(data)
elif topic.startswith("master/"):
# マスターデータを受信
print(f"マスター更新: {data}")
update_master(data)
# 接続時のコールバック
def on_connect(client, userdata, flags, rc):
# 購読するトピック(下り方向)
client.subscribe("config/all")
client.subscribe(f"config/{MY_EQUIPMENT}")
client.subscribe("master/equipment")
print("設定・マスターの購読を開始")
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER, 1883)
# バックグラウンドでメッセージ受信
client.loop_start()
# メインループで設備状態を送信(上り方向)
while True:
status = detect_color() # 色検知
data = {"status": status, "eq_code": MY_EQUIPMENT}
# 状態を送信(Publish)
client.publish(f"equipment/status/{MY_EQUIPMENT}", json.dumps(data))
time.sleep(1)親機(ブリッジ)側のコード例
python
# 親機でOracleデータを子機に配信する例
import paho.mqtt.client as mqtt
import oracledb
import json
import threading
import time
# Oracleからマスターデータを取得して配信
def publish_master_data():
while True:
# Oracleに接続
conn = oracledb.connect(...)
cursor = conn.cursor()
# マスターデータを取得
cursor.execute("SELECT EQ_CODE, EQ_NAME FROM EQUIPMENT_MASTER")
rows = cursor.fetchall()
# 辞書形式に変換
master = {row[0]: row[1] for row in rows}
# MQTTで配信
client.publish("master/equipment", json.dumps(master), retain=True)
print(f"マスターデータを配信: {len(master)}件")
conn.close()
# 1時間ごとに更新
time.sleep(3600)
# 設定変更を配信する関数
def publish_config(eq_code, config):
if eq_code == "all":
topic = "config/all"
else:
topic = f"config/{eq_code}"
client.publish(topic, json.dumps(config))
print(f"設定を配信: {topic}")
# 別スレッドでマスター配信を開始
thread = threading.Thread(target=publish_master_data, daemon=True)
thread.start()ローカルDBとMQTTの比較
そもそもの疑問
「Raspberry Piに直接データベースを入れたらダメなの?」
実は、それでも動きます。でも、MQTTを使う理由があります。
比較表
text
┌─────────────────────────────────────────────────────────────────┐
│ ローカルDB vs MQTT + クラウドDB │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ローカルDB方式 │ │
│ │ (各Piにデータベース) │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ 子機① 子機② 子機③ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 📷 + 💾 │ │ 📷 + 💾 │ │ 📷 + 💾 │ │ │
│ │ │ SQLite │ │ SQLite │ │ SQLite │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ │ ✅ シンプル │ │
│ │ ✅ ネットワーク不要 │ │
│ │ ❌ データが分散(集計が大変) │ │
│ │ ❌ SDカード障害でデータ消失 │ │
│ │ ❌ 子機ごとにバックアップが必要 │ │
│ │ ❌ 全体の状況把握が困難 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ MQTT + クラウドDB方式 │ │
│ │ (中央集権型) │ │
│ ├─────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ 子機① 子機② 子機③ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 📷 のみ │ │ 📷 のみ │ │ 📷 のみ │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ │ └───────────────┼───────────────┘ │ │
│ │ ▼ │ │
│ │ ┌─────────┐ │ │
│ │ │ 中継機 │ │ │
│ │ │Mosquitto│ │ │
│ │ └────┬────┘ │ │
│ │ ▼ │ │
│ │ ┌─────────┐ │ │
│ │ │ ☁️ Oracle│ │ │
│ │ │ 集中DB │ │ │
│ │ └─────────┘ │ │
│ │ │ │
│ │ ✅ データが一箇所に集約 │ │
│ │ ✅ クラウドで自動バックアップ │ │
│ │ ✅ 全体の状況を一覧で把握 │ │
│ │ ✅ 分析・レポートが簡単 │ │
│ │ ❌ ネットワーク必須 │ │
│ │ ❌ 構成がやや複雑 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘具体的な比較
| 観点 | ローカルDB | MQTT + クラウド |
|---|---|---|
| データ集計 | 各Piに接続して手動集計 | SQLで一発 |
| バックアップ | SDカードごとにコピー | クラウドで自動 |
| 障害時 | そのPiのデータ消失 | 他のPiに影響なし |
| 容量 | SDカード依存(8-64GB) | クラウドで無制限 |
| 複数人閲覧 | 各Piにアクセス必要 | 1つのDBを共有 |
| リアルタイム共有 | 実装が複雑 | MQTTで簡単 |
| ネット障害時 | 動作継続 | 一時停止(※1) |
※1: ローカルバッファを持たせれば、ネット復旧後に再送信可能
どちらを選ぶべき?
text
┌─────────────────────────────────────────────────────────────────┐
│ 選択の目安 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 【ローカルDBが向いているケース】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ・子機が1-2台で、データ共有の必要がない │ │
│ │ ・ネットワークが不安定 or ない環境 │ │
│ │ ・データは子機の近くでしか見ない │ │
│ │ ・短期間のテスト・実験 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ 【MQTT + クラウドが向いているケース】 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ・子機が3台以上 │ │
│ │ ・データを一箇所で管理・分析したい │ │
│ │ ・複数人でデータを閲覧したい │ │
│ │ ・長期運用でデータ蓄積が必要 │ │
│ │ ・遠隔から設定変更したい │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ⭐ 迷ったら MQTT + クラウド がおすすめ! │
│ 理由: 後から「やっぱり集約したい」は大変 │
│ 最初から集約しておけば、後で困らない │
│ │
└─────────────────────────────────────────────────────────────────┘ハイブリッド構成(ベストプラクティス)
text
┌─────────────────────────────────────────────────────────────────┐
│ ハイブリッド構成(両方のいいとこ取り) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 子機 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 📷 カメラ │ → │ 💾 SQLite │ ← ローカルバッファ │ │
│ │ └─────────────┘ │ (一時保存) │ │ │
│ │ └──────┬──────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────────┐ │ │
│ │ │ MQTT送信 │ │ │
│ │ │ (成功したら │ │ │
│ │ │ ローカル削除)│ │ │
│ │ └─────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 中継機 → Oracle Cloud │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ メリット: │
│ ✅ ネット障害時もローカルに保存(データ欠損なし) │
│ ✅ 復旧後に自動で再送信 │
│ ✅ 通常時はリアルタイムでクラウド同期 │
│ │
└─────────────────────────────────────────────────────────────────┘まとめ
双方向通信でできること
| 方向 | 用途 | トピック例 |
|---|---|---|
| 上り(子機→クラウド) | 状態送信、アラート | equipment/status/# |
| 下り(クラウド→子機) | 設定配信、マスター | config/#, master/# |
| 横(子機↔子機) | 他設備の状態取得 | equipment/status/# |
ローカルDB vs クラウドDB
| 項目 | ローカルDB | MQTT + クラウド |
|---|---|---|
| 適した規模 | 小規模(1-2台) | 中〜大規模(3台以上) |
| データ管理 | 分散 | 集中 |
| 障害耐性 | 単独完結 | 中継機依存 |
| 拡張性 | 低い | 高い |
この記事のポイント
text
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 1. MQTTは双方向通信ができる │
│ → 子機は「送る」だけじゃなく「受け取る」こともできる │
│ │
│ 2. トピック設計が重要 │
│ → 上り/下り/横 を明確に分ける │
│ │
│ 3. ローカルDBとクラウドDBは使い分け │
│ → 規模と用途で選択 │
│ → 迷ったらクラウド集約がおすすめ │
│ │
│ 4. ハイブリッドが最強 │
│ → ローカルバッファ + クラウド同期 │
│ │
└─────────────────────────────────────────────────────────────────┘