MQTT入門

MQTTを組み込み機器に実装しようとすると、プロトコルの概要だけでは足りない場面が出てきます。

QoS 0/1/2のどれを選ぶか、retainやwillをどう使い分けるか、回線が不安定な現場で再接続をどう設計するか。このように判断に迷うポイントは意外と多く、しかもデータシートには書かれていません。

この記事では、組み込み実装の視点からMQTTの主要機能を整理し、設計時に押さえておきたい勘所をまとめました。

MQTTとは|組み込み機器でMQTTが選ばれる理由

MQTTは、組み込み機器のデータ送受信に向いた軽量プロトコルです。Publish/Subscribe型の構造をとり、送信側と受信側が直接つながるのではなく、ブローカーを経由してやり取りします。送る側は届け先を知らなくていい、まるで配送センターに荷物を預けるような仕組みです。

HTTPとの違いは、ヘッダサイズにはっきり出ます。HTTPでは数百バイトに膨らむことがありますが、MQTTの固定ヘッダは最小2バイトで済みます。加えて、一度つないだ接続を保ったままメッセージをやり取りできるため、帯域の細い回線や電波が途切れやすい現場と相性が良好です。

組み込みの実装から見ると、RAM消費の少なさが地味に効いてきます。RAMが数十KBしかないMCUでも、軽量なクライアントライブラリを載せれば送受信は動きます。TLS対応の実装も選べるので、メモリの制約が厳しい機器ほどHTTPと比べて採用しやすさに差が出てくるところかもしれません。

QoS 0/1/2の違いと選び方

MQTTのQoSは0/1/2の3段階で、メッセージが届くまでの保証レベルが変わります。

組み込み機器では通信量と信頼性のどちらを優先するかで選びますが、ここの見極めが意外とシビアです。判断を誤ると帯域を圧迫するか、データを欠落させるか、どちらかのトラブルを引き起こしてしまいます。

QoS 0:最大1回配信(At most once)

QoS 0は、送ったら送りっぱなしの方式です。ブローカーはACKを返さず、届いたかどうかの確認をしません。そのため通信量は最も少なく済みます。

向いているのは、温度や湿度のように一定間隔で送り続けるセンサデータです。1回分が抜けても数秒後には次の値が届くため、欠落が致命傷にならない場面であれば、このレベルで十分でしょう。

QoS 1:最低1回配信(At least once)

QoS 1は、メッセージが最低1回は届くことを保証する方式です。ブローカーはPUBLISHを受け取るとPUBACKを返し、送信側はACKが届かなければ再送します。QoS 0より確実ですが、通信量はその分多いです。

なお、再送が走ると同じメッセージが2回届くことがあります。たとえばアラームの発報回数をカウントしている仕組みだと、重複のせいで数字がずれかねません。

そのため、同じデータが来ても結果が変わらないよう、受信側の処理を組んでおけるかどうかが、このレベルを選ぶときの判断の分かれ目です。

QoS 2:正確に1回配信(Exactly once)

QoS 2は、メッセージが正確に1回だけ届くことを保証する方式です。

PUBLISH→PUBREC→PUBREL→PUBCOMPの4ステップで送受信を確認するため、重複も欠落も起きません。

ただし、やり取りが4ステップに増える分、通信量とレイテンシは大きくなります。非力なMCUや帯域の細い回線では、この負荷が無視できません。そのため、課金データや制御命令のように「1回抜けたら事故になる」ケースに絞って使うのが現実的です。

retain・will・Clean Sessionの実装上の使いどころ

QoSだけがMQTTの信頼性を支えているわけではありません。retain、will、Clean Sessionの3つは、接続が切れたときや新しいデバイスが加わったときの振る舞いを決める機能です。

これら3つの機能について、詳しく見ていきましょう。

retain:新規Subscriberに最新値を即座に届ける

retainフラグを付けてPublishすると、ブローカーがそのトピックの最新1件を保持し続けます。後からSubscribeしたデバイスにも、接続した瞬間に直近の値が届く仕組みです。

組み込みでの使いどころとしては、デバイスのオンライン/オフラインの状態管理があります。監視画面を開いた瞬間に各機器の状態が表示されないと、「動いているのか止まっているのか分からない」という問い合わせにつながりかねません。しかしretainを使えば、その空白をなくせます。

will(遺言メッセージ):異常切断を他デバイスに通知する

willは、デバイスが予期せず切断されたときに備える機能です。

CONNECT時にWill TopicとWill Messageを設定しておくと、ブローカーが異常切断を検知した時点でそのメッセージを自動的にPublishします。デバイス自身は何もせず、ブローカーが代わりに「落ちました」と知らせてくれる形です。

組み込みでの典型的な使い方は、ゲートウェイ障害の即時通知でしょう。配下に数十台のセンサがぶら下がっているゲートウェイが突然落ちると、どのセンサが生きているかも分からなくなります。しかしwillを設定しておけば、監視サーバー側で障害を即座に拾えるため、復旧判断が早くなるのです。

Clean Session:セッション状態の保持と破棄

Clean Sessionは、再接続時にセッション情報を引き継ぐか捨てるかを決めるフラグです。0に設定すると、前回のSubscriptionや未配信のQoS 1/2メッセージがブローカー側に残り、再接続と同時に復元されます。1にすると、すべてリセットされた状態からやり直しです。

データの取りこぼしを減らすか、MCUのRAMを守るかは判断が分かれるところです。ブローカーにセッションを保持させればデータの取りこぼしは減りますが、復元時にまとめて届くメッセージをMCU側のRAMで受けきれるかという問題が出てきます。

基本的にRAMが数十KBしかない機器では、Clean Session = 1で毎回Subscribeし直すほうが安全な場合が多いでしょう。復元より再登録のほうが、メモリの見通しを立てやすくなります。

再接続とバックオフ設計|不安定な回線で安定運用するために

IoT機器は電波の届きにくい場所や移動中の車両など、回線が安定しない現場で動くことが多く、再接続の設計を省くわけにはいきません。

まず必要なのは切断の検知です。MQTTではKeep Aliveという仕組みで接続の生存を確認します。クライアントが一定間隔でPINGREQを送り、ブローカーがPINGRESPを返す。この応答が途絶えたら「切れた」と判断して再接続に入ります。

再接続の間隔は、指数バックオフで設計するのが基本です。初回1秒、次は2秒、その次は4秒と間隔を倍に広げていき、上限を60秒程度に設定します。一定間隔で再接続を叩き続けると、ブローカー側に負荷が集中して復旧がかえって遅れかねません。

再接続が通った後も、もうひと手間あります。Clean Session = 1で運用している場合、Subscriptionはリセットされているので、接続直後にSubscribeを再登録する処理が必要です。ここを抜かすと、つながっているのにデータが届かないという厄介な状態になる可能性があります。

また、Keep Aliveの間隔設定も地味にハマりやすいところです。短くすれば切断をすぐ検知できますが、モバイル回線ではPINGREQの頻度がバッテリーを削ります。逆に長くしすぎると、切断に気付くまで数分かかることもあるため、機器の電源事情と検知速度のバランスを見て決めるしかありません。

組み込み向けMQTTライブラリの選び方

組み込み向けのMQTTライブラリは数多くありますが、選定の軸はシンプルで、RAM/ROMの制約と必要な機能のどこで折り合いをつけるかです。

よく名前が挙がるのは3つ。Eclipse Paho Embedded CはRTOS非依存で動き、QoS 0/1/2をすべてサポートします。coreMQTTはAWS IoT Device SDKに含まれ、依存が少なくROMフットプリントが小さいのが特徴です。MosquittoはブローカーとクライアントのC実装を兼ねており、Linuxベースのゲートウェイ機器でよく採用されています。

選ぶときにまず確認したいのはTLS対応です。クラウド接続を前提にするなら、Mbed TLSやwolfSSLと組みあわせて動くかが最初のふるいになります。次に見るべきはQoS 2の要否です。欠損が許されないデータを扱わないなら、QoS 1までで足りることも多く、その分コードサイズを削れます。

もう一つ見落としやすいのがRTOS依存です。RTOSを使用することが前提のライブラリをベアメタル環境に持ち込もうとして、移植コストで手が止まるケースは珍しくありません。そのため、先に実行環境を決めてからライブラリを絞るほうが近道です。

組み込みソフトの世界 トップへ戻る