メインコンテンツへスキップ
三田工場 技術サイト
MQTTで双方向通信!Oracleのデータを子機に配信する

MQTTで双方向通信!Oracleのデータを子機に配信する

HowTo16分で読めます

このシリーズ: 全5回

  1. 第1回: MQTTとは?IoT時代の「伝言板」を初心者向けに解説
  2. 第2回: Pub/Subモデルとトピックの仕組み
  3. 第3回: 実践:Raspberry Piで設備監視システムを作る
  4. 第4回: 複数のRaspberry Piを中継機で束ねてOracle DBにデータを送る
  5. 第5回: MQTTで双方向通信!Oracleのデータを子機に配信する ← 今ここ

はじめに

前回までの記事では、子機→親機→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. ハイブリッドが最強                                           │
│     → ローカルバッファ + クラウド同期                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

関連記事

関連記事