注目イベント!
春の新人向け連載企画開催中
新人エンジニアの皆さん、2024春、私たちと一緒にキャリアアップの旅を始めませんか?
IT業界への最初の一歩を踏み出す新人エンジニアをサポートする新連載スタート!
mameyose

IoT を使ってみる(その5:MQTT編)

| 6 min read
Author: shuichi-takatsu shuichi-takatsuの画像

このIoTの連載の中で何度も「MQTT」という言葉が出てきました。
これまでMQTTの詳細について何も触れてこなかったので、今回はMQTTとは何かについて確認していきたいと思います。

MQTT とは

#

MQTTとは「Message Queue Telemetry Transport」の略で、パブリッシュ/サブスクライブ(publish/subscribe)型データ配信モデルのメッセージプロトコルです。
MQTTはTCP/IPを使っており、非常に軽量なプロトコルとして設計されています。

MQTTはIBMが開発した「IBM MQ」がベースになっているようです。
IBM MQからMQTTへの変遷についてはWikipedia等に詳しく記載されています。

パブリッシュ/サブスクライブ(publish/subscribe)型データ配信モデル

#

MQTTはパブリッシュ/サブスクライブ(publish/subscribe)型のデータ配信モデル(以降、PUB/SUBモデルと略す)を採用しています。

PUB/SUBモデルは以下の3つの要素で構成されています。

  • パブリッシャー(publisher): メッセージ送信側クライアント
  • サブスクライバー(subscriber): メッセージ受信側クライアント
  • ブローカー(broker): MQTT基地局としてのサーバ

MQTTではブローカーがパブリッシャーとサブスクライバーの間に入ってメッセージを仲介し、パブリッシャーとサブスクライバー同士が直接メッセージのやり取りをしないことが特徴です。

MQTT通信の構造

上記の例では、トピック「A」で、パブリッシャー(送信側)がMQTTブローカーにPublishし、サブスクライバー(受信側)がMQTTブローカーにSubscribeしています。

トピック

#

MQTTでメッセージの送受信に使われる通信エンドポイントのことを「トピック」と言います。
HTTP通信のURLのようなものだとお考えください。

トピックの形は前回の記事で使用した
esp32/sub

esp32/pub
のように文字列を「/」(スラッシュ)で連結した階層構造を取ることができます。

パブリッシャーは送信先エンドポイント(トピック)を指定し、サブスクライバーは受信先エンドポイント(トピック)を指定します。
HTTP通信ではメッセージの送受信は1対1ですが、MQTTの場合は多対多の送受信が可能です。

メッセージ保証レベル

#

MQTTでは以下の「メッセージ保証レベル(QoS)」を定義しています。

QoSは”Quality of Service”の略であり、メッセージの送達保証レベルを示します。

QoS 保証内容
0 最大1回のメッセージ送信が保証されます。
メッセージ受信は保証されません。
1 最低1回のメッセージ送信が保証されます。
メッセージ受信は保証されますが、
メッセージが重複して受信される可能性があります。
2 確実に1回のメッセージ送受信が保証されます。
未送信や重複受信はありません。

QoS0は一番簡単で通信コストも低いですが、メッセージ送受信の確実性はありません。
QoS1はメッセージが受信されることを保証しますが、通信が不安定な環境だと受信側アプリケーションに多重受信に対する処理が必要になります。
QoS2は確実なメッセージ送受信を実現しますが、通信負荷はかなり上がります。送受信するIoTデバイスの数が少ない場合は問題ないでしょうが、多数のIoTデバイス間でメッセージ送受信をする場合は問題になるかもしれません。

HTTPとの違い

#

MQTTはよくHTTPと比較されます。
HTTPが安定したネットワーク環境での通信を前提にしているのに対し、MQTTは不安定なネットワーク環境や非力なデバイスでも動作するように設計されています。

MQTT通信

HTTP通信

比較項目 MQTT HTTP
同期有無 非同期 同期
送受信の対象 多対多 1対1
通信モデル パブリッシャー/サブスクライバー型 サーバ/クライアント型

MQTTは非同期プロトコルであり、MQTTブローカーを経由することで多対多の通信を実現できます。ネットワークが不安定な環境でも利用可能です。
HTTPは同期プロトコルであり、サーバとクライアントは1対1で通信を行う必要があります。基本的に安定したネットワーク環境で運用されることを想定しています。

IoTのように多数のIoTデバイスが相互にメッセージをやり取りし、接続が不安定なネットワーク環境での利用が想定される場合にはMQTTが適していると考えられます。

MQTTサンプルプログラム(ESP32用)

#

前回はAWS IoTのMQTTクライアントを利用してPUB/SUB通信を試しましたが、毎回AWSアカウントを利用するのは負荷が高いので、今回は簡単にMQTT通信を試すプログラムを用意しました。

Arduino IDEから以下のプログラムをIoTデバイス(以前使用した”ESP32 LOLIN D32”)に登録します。

#include <WiFi.h>
#include <PubSubClient.h>

// WiFi
const char ssid[] = "XXXXX";
const char passwd[] = "xxxxxx";

// Pub/Sub MQTTブローカー
const char* mqttHost = "<IPアドレス or ホスト名>"; // MQTTのIPかホスト名
const int mqttPort = 1883;       // MQTTのポート
const int QOS = 0;                // QoS

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

const char* topic_pub = "topic_pub";     // 送信先のトピック名
const char* topic_sub = "topic_sub";     // 受信先のトピック名
const char* payload_pub = "payload - ESP32 LOLIN D32";  // 送信するデータ

void setup() {
  Serial.begin(115200);

  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);

  // Connect WiFi
  connectWiFi();

  // Connect MQTT
  connectMqtt();
}

void loop() {

  // 送信処理 topic, payloadは定数で定義
  mqttClient.publish(topic_pub, payload_pub);
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(500);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(500);                       // wait for a second

  // WiFi
  if ( WiFi.status() == WL_DISCONNECTED ) {
    connectWiFi(); 
  }
  // MQTT
  if ( ! mqttClient.connected() ) {
    connectMqtt();
  }
  mqttClient.loop();  

}

/**
 * CallBack
 */
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

/**
 * Connect WiFi
 */
void connectWiFi()
{
  WiFi.begin(ssid, passwd);
  Serial.print("WiFi connecting...");
  while(WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.print(" connected. ");
  Serial.println(WiFi.localIP());
}

/**
 * Connect MQTT
 */
void connectMqtt()
{
  mqttClient.setServer(mqttHost, mqttPort);
  mqttClient.setCallback(callback);
  while( ! mqttClient.connected() ) {
    Serial.println("Connecting to MQTT...");
    String clientId = "ESP32-" + String(random(0xffff), HEX);
    if ( mqttClient.connect(clientId.c_str()) ) {
      Serial.println("connected"); 
    }
    mqttClient.subscribe(topic_sub, QOS);
    delay(1000);
    randomSeed(micros());
  }
}

以下のWifiの設定に、自宅/職場で利用できるSSID/パスワードを指定してください。

// WiFi
const char ssid[] = "XXXXX";
const char passwd[] = "xxxxxx";

以下のPUB/SUB MQTTブローカーの設定に、利用できるMQTTブローカーのIPアドレスまたはホスト名を指定してください。
今回はMQTTブローカーとして「Mosquitto」を使用しました。
(Mosquittoについては別の回で紹介します)

// Pub/Sub MQTTブローカー
const char* mqttHost = "<IPアドレス or ホスト名>"; // MQTTのIPかホスト名
const int mqttPort = 1883;       // MQTTのポート
const int QOS = 0;                // QoS

トピック名と送信するメッセージについて以下のように設定しました。(任意のトピック名とメッセージを指定可能です)

const char* topic_pub = "topic_pub";     // 送信先のトピック名
const char* topic_sub = "topic_sub";     // 受信先のトピック名
const char* payload_pub = "payload - ESP32 LOLIN D32";  // 送信するデータ

ESP32でプログラムが実行される(LEDがチカチカと点滅します)と、MQTTブローカーにメッセージが送信されます。

MQTTブローカーのトピック「topic_pub」をサブスクライブした結果、以下のメッセージが受信されました。

また、MQTTブローカーのトピック「topic_sub」にメッセージ「"hello! MQTT"」をパブリッシュした結果、ESP32側に以下のメッセージが受信されたことが確認できました。(Arduino IDEのシリアルモニタで確認)

まとめ

#

MQTTの仕組みと特徴をご説明しました。
またIoTデバイスにESP32を使用し、簡単なMQTTパブリッシャーとサブスクライバーを実装しました。
次回は、MQTTブローカー「Mosquitto」についてご紹介したいと思います。

IoTに関するチュートリアルや実践テクニックをまとめています。

IoT活用の参考になれば幸いです。

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。