0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MQTTとC++でJSON形式を扱ってみる

Posted at

筆者はRaspberry Piでオートロックシステムというものを開発しており、その制御のためにAPIを作成したかったのですが、MQTTとJSONを組み合わせれば楽なのではないかということを思いついたため、その方法を記します。

MQTTライブラリの導入

まず、使用するMQTTのライブラリですがmosquittoを使用します。導入方法は以下です。
mosquittoはブローカーを立ち上げるためのパッケージ、clientsはMQTTを送受信するためのパッケージです。

sudo apt install mosquitto mosquitto-clients

mosquittoはデフォルトで外部からのアクセスを許可していないので、/etc/mosqitto/mosquitto.confの最終行にanonymous trueと追記します。

sudo sh -c "echo 'allow_anonymous true' >> /etc/mosquitto/mosquitto.conf"

また、ポートはデフォルトで1883を使用しますが、そのほかのポートを使用したい場合は
listener ポート番号という感じで任意のポート番号を指定して追記してください。

mosquittoで送受信の練習

mqttには送信者(Publisher)、仲介者(Broker)、受信者(Subscriber)の3者が必要になります。
mosquitto.serviceを起動すればブローカーが機能します。

送信

送信はmosquitto_pubコマンドを使用します。以下のコマンドは送信の例です。
-tオプションでトピック名、-mでメッセージ、-hでブローカーのipアドレスを指定します。
ちなみに-hを指定しなければ、デフォルトでlocalhostがブローカーとして指定されます。

mosquitto_pub -t '/test/topic' -m 'test message' -h 192.168.0.1

受信

受信側も送信の際と同様に、受信したいトピック名とブローカーのipアドレスを指定することによってメッセージを受け取ります。こちらも-hを指定しなければlocalhostから受け取ろうとします。

mosquitto_sub -t '/test/topic' -h 192.168.0.1

詳しくはこちらのページがわかりやすいと思います。

mosquittoでjsonメッセージを扱ってみる

JSON形式とは、JavaScript Object Notationの略で、人間にも機械にも読みやすい軽量なデータ交換用フォーマットです。主にWebアプリケーションやAPI間のデータ交換で使われています。

今回オートロックの操作をするにあたりAPIはなんでもよかったのですが、JSONが扱いやすそうということでJSON形式を採用しました。
以下にJSONの基本フォーマットを示します。

{
  "キー1": "文字列の値",
  "キー2": 123,             // 数値
  "キー3": true,            // 真偽値(true/false
  "キー4": null,            // null
  "キー5": [1, 2, 3],       // 配列
  "キー6": {
    "サブキー": "入れ子オブジェクト"
  }
}

JSON形式のルール

  • キーと文字列は必ずダブルクォーテーション" "で囲む
  • 配列は [ ]、オブジェクトは { }
  • true, false, null は小文字で書く(大文字不可)
  • 最後の要素に カンマ , を付けない

以下の例を見ればどのように使うものかわかりやすいと思います。

{
  "name": "Taro",
  "age": 25,
  "isStudent": false,
  "skills": ["Python", "JavaScript", "C++"],
  "address": {
    "city": "Tokyo",
    "zip": "100-0001"
  }
}

要素に複数の値を格納したい場合は[]を使用し、要素の中に小要素を入れたい場合は{}を使用するといった感じです。

MQTTでJSONの送信

では、いよいよこちらをmosquittoで送信してみたいと思います。
すべて書くと長くなるので、一部省略しています。

mosquitto_pub -t '/test/topic' -m '{"name": "Taro","skills":["Python","C++"],"address":{"city":"Tokyo"}}'

subscriber側では以下のようなメッセージが受け取れます。

{"name": "Taro","skills":["Python","C++"],"address":{"city":"Tokyo"}}

メッセージをC++で受信する

mqttで送信されたメッセージをC++で受け取ってみましょう。
新たにlibmosquittoというライブラリの導入をします。

sudo apt install libmosquitto-dev
mqtt_sub.cpp
#include <mosquitto.h>
#include <iostream>
#include <cstdlib>
#include <cstring>

void on_connect(struct mosquitto *mosq, void *obj, int rc) {
    if (rc == 0) {
        std::cout << "Connected to broker." << std::endl;
        //接続後に一度subscribeを呼び出す必要がある
        mosquitto_subscribe(mosq, NULL, "/test/topic", 0);
    } else {
        std::cerr << "Failed to connect, return code " << rc << std::endl;
    }
}

void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) {
    std::cout << "Received message: " << (char*)msg->payload << " on topic: " << msg->topic << std::endl;
}

int main(int argc, char *argv[]) {
    mosquitto_lib_init();

    struct mosquitto *mosq = mosquitto_new(NULL, true, NULL);
    if (!mosq) {
        std::cerr << "Failed to create mosquitto instance." << std::endl;
        return 1;
    }

    //コールバック関数の設定
    mosquitto_connect_callback_set(mosq, on_connect);
    mosquitto_message_callback_set(mosq, on_message);

    //mosquittoに接続、引数->(インスタンス,ブローカー,接続ポート,接続を確認する間隔)
    int rc = mosquitto_connect(mosq, "localhost", 1883, 60);
    if (rc != MOSQ_ERR_SUCCESS) {
        std::cerr << "Unable to connect: " << mosquitto_strerror(rc) << std::endl;
        return 1;
    }

    mosquitto_loop_start(mosq);  // 非同期ループ
    std::cout << "Press Enter to exit..." << std::endl;
    std::cin.get();

    //終了処理
    mosquitto_loop_stop(mosq, true);
    mosquitto_destroy(mosq);
    mosquitto_lib_cleanup();

    return 0;
}

※on_connect関数内で、subscribeを呼び出していますが、接続後に一度subscribeを呼び出さないと、メッセージを受け取れません。複数のトピックを受け取りたい場合は、そのトピック分subscribeを呼び出す必要があります。

libmosquittoをリンクして、上記のコードをコンパイルします。

g++ -o mqtt_sub mqtt_sub.cpp -lmosquitto

コンパイルしたコードを実行し、例の通りメッセージを送信すると以下のようになると思います。

Press Enter to exit...
Connected to broker.
Received message: {"name": "Taro","skills":["Python","C++"],"address":{"city":"Tokyo"}} on topic: test/topic

これで、C++でのメッセージ受信は成功です。

C++でJSON形式の処理

C++でメッセージを受信できたので、受信したJSONを扱っていきます。
JSONを扱うためのC++ライブラリとしてnlohmann/jsonというライブラリが良さそうであるため、こちらを使用します。

導入は下記コマンドで行うか、gitから直接落としてビルドしても良いです。

sudo apt install nlohmann-json3-dev

先ほど示したコードにJSONの処理を追加しました。
ライブラリを読み込んで、受け取った文字列をnlohmann::jsonで扱えるJSON形式に変換すると、配列のように要素を指定して、値にアクセスできるようになります。

#include <mosquitto.h>
#include <iostream>
#include <cstdlib>
#include <cstring>
+ #include <nlohmann/json.hpp>

void on_connect(struct mosquitto *mosq, void *obj, int rc) {
    if (rc == 0) {
        std::cout << "Connected to broker." << std::endl;
        mosquitto_subscribe(mosq, NULL, "/test/topic", 0);
    } else {
        std::cerr << "Failed to connect, return code " << rc << std::endl;
    }
}

void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) {
    std::cout << "Received message: " << (char*)msg->payload << " on topic: " << msg->topic << std::endl;
    
+    //文字列をnlohmann::jsonで扱えるように変換(パース)
+    auto jobj = nlohmann::json::parse((char*)msg->payload);

+    std::cout<<jobj["name"]<<std::endl;
+    std::cout<<jobj["skills"][0]<<std::endl;
+    std::cout<<jobj["address"]["city"]<<std::endl;
}

int main(int argc, char *argv[]) {
    mosquitto_lib_init();

    struct mosquitto *mosq = mosquitto_new(NULL, true, NULL);
    if (!mosq) {
        std::cerr << "Failed to create mosquitto instance." << std::endl;
        return 1;
    }

    mosquitto_connect_callback_set(mosq, on_connect);
    mosquitto_message_callback_set(mosq, on_message);

    int rc = mosquitto_connect(mosq, "localhost", 1883, 60);
    if (rc != MOSQ_ERR_SUCCESS) {
        std::cerr << "Unable to connect: " << mosquitto_strerror(rc) << std::endl;
        return 1;
    }

    mosquitto_loop_start(mosq);  // 非同期ループ
    std::cout << "Press Enter to exit..." << std::endl;
    std::cin.get();

    mosquitto_loop_stop(mosq, true);
    mosquitto_destroy(mosq);
    mosquitto_lib_cleanup();

    return 0;
}

コンパイルをします。gitから落としている場合インクルードディレクトリの指定が必要になると思うので-Iで指定します。※aptで入れた場合は-I以下は不要

g++ -o mqtt_sub mqtt_sub.cpp -lmosquitto -Ijson/include

出力は以下のようになります。しっかり指定した要素の値が表示できていることがわかりました。

Press Enter to exit...
Connected to broker.
Received message: {"name": "Taro","skills":["Python","C++"],"address":{"city":"Tokyo"}} on topic: /test/topic
"Taro"
"Python"
"Tokyo"

これでMQTTでJSONメッセージを送信し、C++で処理することができました。

筆者のオートロックシステムではこれを利用し、データベースの情報と照合することで操作をできるようにしています。そして、これのいいところは、LINEやSlackでメッセージを受け取るプログラムを作成し、メッセージからJSONに整形し、それをMQTTで送信することによって、メインのプログラムは何も変更することなく、機能を拡張できるということです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?