MQTTクライアントを使うことで、C言語など様々な言語でMilkcocoaを利用できるようになりました

Posted: / Tags: MQTT C言語



先日、Milkcocoaを「MQTTベース」にアップデートしました。MQTTベースとはどういうことかというと、MQTTが使える環境であればどこからでもMilkcocoaを利用することが出来るようになったということです。

※Swiftでは、TLS通信を使うと接続できないので注意して下さい。

Paho」というMQTTクライアントのオープンソースプロジェクトには以下の言語が用意されているので、少なくともこれらの言語では使えるようになるということです。

  • C言語
  • Java
  • Android
  • Python
  • C/C++ MQTT Embedded clients
  • .Net and WinRT
  • Go言語

Milkcocoaで利用するためには、以下のフォーマットでMQTT通信を行います。どの言語でも共通です。(説明の中で出てくるapp_idとは、Milkcocoaで作成したアプリが持っている固有のIDのことです)

  • ホスト(接続先URL)はTLS通信(暗号化通信)であればmqtts://app_id.mlkcca.com:8883、TLSなしであればmqtt://app_id.mlkcca.com:1883
  • 接続の際のオプションで、usernameにトークン(ログインが必要ない場合は'sdammy')、passwordにアプリのapp_idを設定する
  • トピック名は、app_id/データストアのパス名/メソッド名(例えば、messageデータストアにpush()したい場合のトピック名はapp_id/message/push
  • Publishするときは、{params: {任意のkey: 任意のvalue, ...}}のように、必ずparamsの中にデータを記述する

では具体的にC言語でどうやって使い始めるか見ていきましょう。

C言語がわからない方は、以下にNode.jsでの例を用意したので、そちらで理解頂ければと思います。

ソースコード・使うライブラリ

ソースコードは以下に用意しています。

使うライブラリは、先ほど紹介したPahoのCクライアントを利用します。

MQTTクライアントをビルド

まず始めに、MQTTクライアントをビルドします。Pahoのリポジトリからクローンして、プロジェクトディレクトリへ移動します。

git clone http://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.c.git
cd org.eclipse.paho.mqtt.c

ビルドをする前に、Macの場合は先にMakefileの230行目をコメントアウトしてください。

Makefile
...
#/sbin/ldconfig $(DESTDIR)${libdir}
...

コメントアウトしたら、ビルドします。

make
sudo make install

これでMQTTクライアントを使い始める準備ができました。

サンプルコードをコンパイルして動かす

サンプルコードをコンパイルして動かしてみましょう。

MQTTクライアントをまずソースコードをGithubからクローンします。

cd ..
git clone git@github.com:milk-cocoa/c-examples.git
cd c-examples

app.hを自分のアプリ用に修正します。YOUR_APP_IDの部分を自分のアプリのものに書き換えます。TOKEN"sdammy"にしておいてください。

app.h
#define TOKEN "sdammy"
#define APPID "YOUR_APP_ID"
#define HOST "YOUR_APP_ID.mlkcca.com"
#define PORT "1883"

修正が終わったら、以下のコマンドでコンパイルできます。

cc onpush.c -o onpush -lpaho-mqtt3c -lpthread -L ../org.eclipse.paho.mqtt.c/build/output

../org.eclipse.paho.mqtt.c/build/output は最初にMQTTクライアントをビルドした場所の/build/outputか、make installで生成物がインストールされた先のパスを指定してください。

また、PahoのMQTTクライアントをビルドしたフォルダの/build/output以下にMQTTのダイナミックリンクライブラリが作成されます。

そこにあるlibpaho-mqtt3a.so.1libpaho-mqtt3c.so.1c-examples以下にコピーしてください。サンプルを実行するときに必要です。

コピーをしたら、実行してみましょう。

./onpush

topic is (あなたのアプリのID)/message/pushというメッセージが出たら成功です。

onpush.cはpushの監視を行うだけなので、messageデータストアにJSクライアントなど別の方法で適当なデータをpush()してみてください。

面倒な場合はYOUR_APP_IDに、Milkcocoaのサンプルチャットのapp_id、flagi9edsvtgを使って、チャットを投稿して動作確認することをおすすめします。

同様にpush.cをコンパイルして実行すれば、pushが実行できます。JSクライアントなどで"message"データストアを監視したり、アプリの管理画面で確認してみて下さい。

push.cのコードを簡単に説明

動作確認方法の説明が終わったので、push.cのコードを簡単に説明したいと思います。

push.c
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "MQTTClient.h"
#include "app.h"

#define CLIENTID    "ExampleClientPub1"
#define PAYLOAD     "{\"params\":{\"content\":\"from C\"}}"
#define QOS         0
#define TIMEOUT     10000L

int main(int argc, char* argv[])
{
  MQTTClient client;
  MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
  MQTTClient_message pubmsg = MQTTClient_message_initializer;
  MQTTClient_deliveryToken token;
  int rc;
  char url[100];
  char topic[128];

  sprintf(url, "%s:%s", HOST, PORT);

  MQTTClient_create(&client, url, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
  conn_opts.keepAliveInterval = 20;
  conn_opts.cleansession = 1;
  conn_opts.username = TOKEN;
  conn_opts.password = APPID;

  if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
  {
    printf("Failed to connect, return code %d\n", rc);
    exit(-1);
  }
  pubmsg.payload = PAYLOAD;
  pubmsg.payloadlen = strlen(PAYLOAD);
  pubmsg.qos = QOS;
  pubmsg.retained = 0;

  create_push_topic(topic, "message");

  MQTTClient_publishMessage(client, topic, &pubmsg, &token);
  printf("Waiting for up to %d seconds for publication of %s\n"
            "on topic %s for client with ClientID: %s\n",
            (int)(TIMEOUT/1000), PAYLOAD, topic, CLIENTID);

  rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
  printf("Message with delivery token %d delivered\n", token);

  MQTTClient_disconnect(client, 10000);
  MQTTClient_destroy(&client);
  return rc;
}

まずマクロ部分ですが、app.hpush.cに存在するマクロをまとめると以下のようになります。

#define TOKEN "j{JSON Web Token}"
#define APPID "{AppID}"
#define HOST "{AppID}.mlkcca.com"
#define PORT "1883"
#define CLIENTID    "ExampleClientPub1"
#define PAYLOAD     "{\"params\":{\"content\":\"from C\"}}"
#define QOS         0

TOKEN

MQTTプロトコルではusernameの所に設定します。 TOKENはプレフィックスとしてsとjから始まり、s{SessionID}とj{JSON Web Token}というフォーマットを受け付けます。

SessionIDはMilkcocoaのAPIであるauthWithToken()というAPIの戻り値です。JSクライアント以外では、あまり利用しません。

JSON Web TokenはMilkcocoaの管理画面のシークレットトークンをもとに生成されたJWTを設定します。ログインが必要ない場合はTOKENにはsdammyと設定してください。

APPID

APPIDにはあなたが作成したMilkcocoaアプリのIDを設定します。これはMQTTプロトコルのpasswordに設定します。

HOSTとPORT

HOSTはアプリごとに作成されます。app_id.mlkcca.comです。PORTはTLS通信の場合は8883、TLS無しの場合は1883です。

CLIENTID

CLIENTIDは任意の文字列を設定してください。注意点としては、デバイスごとに異なる名前を設定してください。名前がかぶると定期的に接続が切れるという症状が出てしまいます。

PAYLOAD

PAYLOADは実際に送るデータです。JSONフォーマットで送ります。必ずparamsというkeyを作りその中にデータを入れます。

QOS

QOSはMQTTのメッセージ到達品質です。0,1,2の3つとも設定可能です。

TOPIC

TOPICというマクロはありませんが、以下の関数でTOPICを生成しています。

void create_push_topic(char* topic, char* path) {
  sprintf(topic, "%s/%s/push", APPID, path);
}

TOPICのフォーマットは、{AppID}/{データストア名}/{push|send|set|remove}です。

通信部分

マクロ部分の説明が終わったので、実際に通信を行うプログラムの方を見ていきます。

とはいえ、主要な部分だけ抜き出すと以下の3つだけになります。

push.c
// ① 情報を元に接続先を作成して'client'に代入
MQTTClient_create(&client, url, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL);

// ② clientとオプションを使って接続開始
if ((rc = MQTTClient_connect(client, &conn_opts) ...

// ③ 接続先(client)、トピック(topic)、メッセージ(pubmsg)を使って、メッセージをパブリッシュ
MQTTClient_publishMessage(client, topic, &pubmsg, &token);

やりたいことは、「接続して」「メッセージをパブリッシュする」だけなので、そこさえおさえておけばさほど混乱することはないかと思います。

C言語で多様なIoTデバイスで利用可能に

MQTTクライアントを利用すれば、様々な言語からMilkcocoaに接続することが出来ます。

特にC言語はArduinoやmbedといったマイコンボードに使用されているため、そういった端末からもMilkcocoaが利用出来るようになります。

今回のアップデートでさらにIoTで活用できる幅が広がったかと思うので、是非試して頂けると幸いです。