Thing-IFのWindows環境への移植

今回は、Kii Cloud の Thing-IF SDK を Windows PC に移植する方法を紹介します。Visual Studio 2015 Community Edition でビルドすることで、Windows OS 上で Thing-IF SDK と Thing SDK Embedded が動作するようになります。

前回のブログで取り上げた 犬用遠隔おやつあげ機犬用トイレ使用中センサー は、IoT デバイスの代わりに Windows PC を使って構築しました。Kii の IoT 向け SDK は Embedded Linux をリファレンス環境としているため、SDK の移植作業が必要でした。

今回は Windows に移植する方法を採り上げますが、同様の方法によって他のプラットフォームへも容易に移植できるのではないかと思います。

なお、今回は簡易的に SSL を使わない HTTP 通信で Kii Cloud と通信しています。

Thing-IF SDK

Thing-IF は Kii Cloud の IoT 向けフレームワークです。スマートフォンからの IoT デバイス(Thing と呼んでいます)の制御や、状態値(ステート)の参照が簡単にできます。

Thing-IF SDK では、内部で Thing SDK Embedded と呼ばれる Kii Cloud 用のクライアントライブラリを使用しています。この SDK を使うと、Kii Cloud が提供しているデータストアやユーザー、モノ管理などの機能要素を直接使用することができます。

Windows 側で動作している監視プロセスの内部構造は以下のような感じです。

これらの SDK は、オープンソースとして github で公開されているため、ソースの一部をカスタマイズして Windows で動作するようにします。Thing-IF SDK 内にソケットとタスク生成(スレッド生成)のモジュールがあり、この部分を Windows OS に依存した実装に変更すると、SDK を利用できるようになります。

Thing-IF の移植方法

Thing-IF を Windows に移植するには、SDK のソースをダウンロードして Visual Studio のプロジェクトに組み込みます。本来はライブラリ化して組み込むべきでが、今回は手軽にアプリケーションコード中の一部として組み込んでしまいます。

Windows に移植する場合、以下の手順が追加で必要になります。順に紹介します。

  1. Visual Studio プロジェクトの作成
  2. 必要なファイルの抽出
  3. Visual Studio プロジェクトの設定
  4. OS 依存の実装
  5. SDK の実装の調整

Visual Studio プロジェクトの作成

まずは、Visual Studio でプロジェクトを作成します。

今回は Visual Studio 2015 Community Edition を使用します。

C++ プロジェクトなら基本的にどれでもビルドできるはずですが、私は Win32 コンソールアプリケーションのモードで「ThingIF」というプロジェクトを新規作成しました。

main() 関数だけの空のプロジェクトが作成できます。軽くビルドして実行できるかどうか試しておきます。

必要なファイルの抽出

次に、SDK のソースを抽出して Visual Studio に組み込みます。

ひとまず、Linux 環境上で SDK をビルドします。公式ドキュメント の「ライブラリのビルド」までの手順を一通り実行します(事前準備にある gcc、openssl などの準備も忘れずに)。libkiithingifsdk.so がビルドできれば完了です。

次に、以下のファイルを探して Visual Studio のプロジェクトディレクトリにコピーします。Visual Studio では、どのディレクトリにソースがあってもビルドできるのですが、ここではソースが入っているディレクトリの直下に kii というディレクトリを作って、そこにコピーすることにします。転送先ディレクトリは、後で Visual Studio の設定を変更する作業に備えて、階層を作らず、フラットにコピーしました。

Thing-IF SDK Ver.0.9.0 の場合、以下のファイルが必要です。

jsmn.c
jsmn.h
kii.h
kii_call.c
kii_json_utils.c
kii_json_utils.h
kii_mqtt.c
kii_mqtt.h
kii_object.c
kii_push.c
kii_server_code.c
kii_task_callback.h
kii_thing.c
kii_core.c
kii_core.h
kii_libc_wrapper.c
kii_libc_wrapper.h
kii_socket_callback.h
kii_json.c
kii_json.h
kii_thing_if.c
kii_thing_if.h
kii_thing_if_environment_impl.h

手前味噌ですが、Linux などの SSH からのファイルコピーには、ShellFiler が便利です!

Visual Studio プロジェクトの設定

Visual Studio に戻って、次の設定を行います。

  1. ソースの認識

    ソリューションエクスプローラを開いて ソースファイルヘッダーファイル の直下にそれぞれ kii というフィルターを作成します。右クリックして、追加 > 新しいフィルター を実行します。

    次に作成された kii フィルターにコピーしたファイルすべてを読み込ませます。

    ソースファイル > kii 上で右クリックして、追加 > 既存の項目 で、種類順にソートして *.c ファイルを選択して読み込ませます。ヘッダーファイルも同様です。

    こんな感じになるはずです。

  2. プロジェクトの設定

    次に、ソリューションエクスプローラの ThingIF のプロパティから以下を変更します。

    はじめにプロパティの 構成すべての構成 としておくと、Debug と Release の両方を同時に変更できます。

    • 全般 > 文字セット:マルチ バイト文字セットを使用する
    • C/C++ > 追加のインクルードディレクトリ:$(SolutionDir)ThingIF\kii ※ThingIF はプロジェクト名です。
    • リンカー > 入力:ws2_32.lib; を追加
  3. ソースファイルの設定

    ソースファイル > kii 以下のすべてのファイルを選択し(Shift + クリックで)、右クリックしてプロパティを表示します。

    次の設定を行います。

    • C/C++ > プリプロセッサ: ;_CRT_SECURE_NO_WARNINGS;KII_JSON_FIXED_TOKEN_NUM=256 を追加
    • C/C++ > プリコンパイル済みヘッダー:プリコンパイル済みヘッダーを使用しない

    プリプロセッサは、sprintf などの古い標準関数を使うための警告削除のものと、公式ドキュメントに記載されている JSON 解析ライブラリーでのトークン用バッファサイズです。

ここまでで一度ビルドすると、コンパイルが通って、リンカーがシンボルが見つからないというエラーを大量に出してくるはずです。次に、これらを実装します。

OS 依存の実装

Thing-IF SDK とそのコア実装である Thing SDK Embedded は、OS に依存した処理を切り出せる仕組みになっています。

OS に依存した処理とは、次の表の通りです。kii_thing_environment_windows.c として実装しました(先ほどと同様、プリプロセッサ等の設定が必要です)。

分類 実装する関数名 処理
通信処理 socket_connect_cb_impl サーバへのソケットの接続を開始する。
socket_send_cb_impl 指定されたバッファのデータを送信する。
socket_recv_cb_impl 指定されたバッファに受信データを読み込む。
socket_close_cb_impl ソケットの接続を閉じる。
mqtt_connect_cb_impl socket_connect_cb_iml と同じ(MQTT 用)
mqtt_send_cb_impl socket_send_cb_impl と同じ(MQTT 用)
mqtt_recv_cb_impl socket_recv_cb_impl と同じ(MQTT 用)
mqtt_close_cb_impl socket_close_cb_impl と同じ(MQTT 用)
タスク処理 task_create_cb_impl タスクの生成する。
delay_ms_cb_impl タスクのスリープを行う。
ログ出力処理 logger_cb_impl printf フォーマットでのログ出力する。

実装内容は次のような感じです。

  • 通信処理は、HTTP でサーバにリクエストするための関数と、MQTT で通信するための関数があります。今回はいずれも SSL を使用しないため、同一の実装として WinSock の API を呼び出しています(ライブラリに ws2_32.lib も組み込みました)。

  • タスク系は、Windows の場合、_beginthreadex による CRT のスレッド生成と、Win32 の Sleep によるスレッドの一時停止で実装します。スレッド生成には、C のランタイムが使えるよう、_beginthreadex を使うとよいと思います。

  • ログは、簡易的に printf にそのまま中継してコンソール出力することにしました。

OS 依存の処理は、一度作ってしまえば環境ごとに使い回しがききます。Windows で使いたい場合は、今回私が作った github のソースを使い回せばすぐに使えると思います。

以上でビルドすれば、エラーなしの状態になっていると思います。

SDK の実装の調整

最後に SDK の実装を少し修正します。

標準の実装では SSL を使用しているため、ポート 443 に接続します。今回は HTTP で接続するため、ポートを 80 に変更する必要があります。

定義している箇所は kii_core.h の初めの方です。KII_SERVER_PORT の定義を以下のように変更します。

/** Port number of target service.
 *
 * This macro becomes activate, if KII_USE_CUSTOM_HTTP_CLIENT is not
 * defined.
 */
#define KII_SERVER_PORT 80

ビルドとテスト

以上の作業で、プロジェクトに SDK を組み込んだのと同じ状態になります。

main 関数に簡単なテストコードを書いて実行してみます。

必要最低限の main 処理は以下のような感じです。

#include "stdafx.h"
#include <windows.h>
#include "kii_thing_if.h"

#define COMMAND_HANDLER_BUFF_SIZE 4096
#define STATE_UPDATER_BUFF_SIZE 4096
#define MQTT_BUFF_SIZE 2048
#define STATE_UPDATE_PERIOD 60

kii_thing_if_command_handler_resource_t command_handler_resource;
kii_thing_if_state_updater_resource_t state_updater_resource;
char command_handler_buff[COMMAND_HANDLER_BUFF_SIZE];
char state_updater_buff[STATE_UPDATER_BUFF_SIZE];
char mqtt_buff[MQTT_BUFF_SIZE];
kii_thing_if_t kii_iot;

kii_bool_t state_handler(kii_t* kii, KII_THING_IF_WRITER writer)
{
    if ((*writer)(kii, "{}") == KII_FALSE) {
        return KII_FALSE;
    }
    return KII_TRUE;
}

kii_bool_t action_handler(const char* schema, int schema_version, const char* action_name, const char* action_params, char error[EMESSAGE_SIZE + 1])
{
    printf("schema=%s, schema_version=%d, action name=%s, action params=%s\n",
        schema, schema_version, action_name, action_params);
    return KII_TRUE;
}

int main()
{
    kii_bool_t result;

    command_handler_resource.buffer = command_handler_buff;
    command_handler_resource.buffer_size = sizeof(command_handler_buff) / sizeof(command_handler_buff[0]);
    command_handler_resource.mqtt_buffer = mqtt_buff;
    command_handler_resource.mqtt_buffer_size = sizeof(mqtt_buff) / sizeof(mqtt_buff[0]);
    command_handler_resource.action_handler = action_handler;
    command_handler_resource.state_handler = state_handler;

    state_updater_resource.buffer = state_updater_buff;
    state_updater_resource.buffer_size = sizeof(state_updater_buff) / sizeof(state_updater_buff[0]);
    state_updater_resource.period = STATE_UPDATE_PERIOD;
    state_updater_resource.state_handler = state_handler;

    result = init_kii_thing_if(&kii_iot, "___APP_ID___", "___APP_KEY___", "JP", &command_handler_resource, &state_updater_resource, NULL);
    if (result == KII_FALSE) {
        printf("Failed to thing-if initialization");
        return 1;
    }

    result = onboard_with_vendor_thing_id(&kii_iot, "1111", "2222", "PC", NULL);
    if (result == KII_FALSE) {
        printf("Failed to thing-if onboard");
        return 1;
    }
    while (1) {
        Sleep(1000);
    }

    return 0;
}

AppID と AppKey の組み込みが必要です。

実行すると、スリープ状態になります。

vendorThingID が 1111、パスワードが 2222 で登録されていますので、クライアント側から同じ条件で初期登録すれば、この Thing にコマンドを送信できると思います。

まとめ

以上、Thing-IF を Windows で実行する手順でした。

公式ドキュメントでは Embedded Linux での利用方法しか示していませんが、今回の方法を使えばどのような環境にも比較的簡単に移植できるはずです。Thing-IF SDK や Thing SDK Embedded は、その名前の通り、元は組み込み環境向けに使えるように設計されているため、高い移植性があると思います。C コンパイラが動作して、TCP/IP とマルチタスクをサポートした環境であれば、ほぼ移植できると思ってよいと思います。

Thing-IF SDK が移植できれば、Thing-IF の基本機能以外にも、様々な機能を利用できます。例えば、 犬用トイレ使用中センサー の例では、コアとなっている Thing SDK Embedded を直接使う行う例や、REST API を使う例を示しています。

このページの情報が、移植作業の助けになれば幸いです。