目次


位置認識機能を備えた IoT アプリを作成する、第 2 回

ジオターゲティング広告を IoT デバイスに配信する PHP アプリを作成する

IBM Watson IoT Platform、IBM Cloud、MQTT を利用して、モノのインターネット対応の PHP アプリを開発する

Comments

コンテンツシリーズ

このコンテンツは全2シリーズのパート#です: 位置認識機能を備えた IoT アプリを作成する、第 2 回

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:位置認識機能を備えた IoT アプリを作成する、第 2 回

このシリーズの続きに乞うご期待。

テクノロジーとビジネスにおいて、IoT デバイスおよび IoT アプリケーションは大きな可能性を持っています。それは、複数の IoT デバイスからのデータ・ストリームを IoT アプリケーションによってリアルタイムで結合し、システムに対する影響を分析した上で、その結果に対して自動的かつインテリジェントに応答することが可能になるからです。外部のイベントに対して適切な応答を迅速かつ効率的に返すには、前線にあるセンサーやデバイスと、バックオフィス内のルール、フィルター、ビジネス・ロジックとの間の双方向通信が重要な鍵となります。

前回の記事では IBM Watson IoT Platform を紹介し、このサービスを IoT デバイスの生成するデータ・ストリームに接続することで、有用なアプリケーションを作成できることを説明しました。その一例として、IBM Cloud 上で稼働する PHP アプリケーションに IBM Watson IoT Platform を統合して Android スマートフォンからの GPS データを受信し、そのデータに基づいて、該当するデバイスの位置を Web ブラウザーでリアルタイムに追跡する方法を説明しました。

ただし、IBM Watson IoT Platform を利用して実行できることはそれだけではありません。IBM Watson IoT Platform には他にも興味深い機能がたくさんあります。例えば、データ処理を自動化するためのフィルターとアクション、そしてデバイスにコマンドをパブリッシュするためのサポートです。これらの機能を使用すれば、IoT デバイスと IoT アプリケーションとの間の双方向通信を実装することも可能です。つまり、IoT アプリケーションが IoT デバイスからの着信データ・ストリームを読み取って、そのデータを有用な方法で処理し、それによって生成された情報やコマンドを IoT デバイスに送信して「何らかのアクション」を実行させることができます。

このチュートリアルでは、このアイデアに基づいて IBM Cloud アプリケーションを作成する方法を説明します。このアプリケーションには IBM Watson IoT Platform を統合し、Android スマートフォンから GPS データを受信できるようにします。そして GPS データを使用してデバイスの位置を特定し、デバイスとの近接性に基づいてマーケティング・オファーをそのデバイスに送信します。

サンプル・アプリの目的はかなり単純で、自宅の近所や街に出掛けているときに、現在地の近くに有効なマーケティング・オファー、プロモーション、またはイベントがある場合、スマートフォンがそれを知らせるようにするというものです。つまり、例えばショッピング・モールに向かっているときに、ランチのピザが 25% 割引になるクーポン付きの通知を受信したり、映画館に到着すると、近くのバーでライブ・ミュージックのイベントがあることを通知されたりするなど、現在地に関連する局所的なオファーをリアルタイムで受け取ることができます。また、プロモーターにとっては、直接視線に触れることがなかった新しい見込み客に近づく手段にもなります。

このアプリケーションは、次のように機能します。

  • まず、IoT Starter Application for Android を使用して Android フォンを GPS センサーに変身させることで、Android スマートフォンが自身の位置を常時 IBM Watson IoT Platform にパブリッシュするようにします。
  • 次に、IBM Watson IoT Platform を利用してデバイスからの位置データをモニターします。デバイスが一定の時間移動していないと判断されたら、IBM Cloud 上で稼働している PHP アプリケーションをトリガーします。
  • PHP アプリケーションがトリガーされると、IBM Cloud アプリケーションがデータベースを調べて、デバイスから報告された位置の近くで有効なマーケティング・オファーを検索します。一致結果を検出すると、IBM Watson IoT Platform を利用して IoT デバイス (Android スマートフォン) にアラートを送信します。
  • スマートフォン上の IoT Starter Application for Android はアラートを受信すると、ユーザーに通知を送信します。

この IoT アプリケーションを作成するために必要なもの

このアプリケーションの作動部分は、ごくわずかです。したがって、以下の短いリストに挙げられているものが揃っていれば、このアプリケーションを作成できます。

今回開発する PHP アプリは前回のチュートリアルで作成したものとかなり似ているので、このチュートリアルの手順を開始する前に、前回のチュートリアルで説明した手順を完了してください。具体的には、以下のステップを完了する必要があります。

  • ステップ 1: IBM Cloud アプリを作成します。作成したアプリに、Internet of Things Platform サービス・インスタンスがバインドされます。
  • ステップ 2 と 3: Android スマートフォンを IBM Watson IoT Platform 組織に登録し、IoT Starter Application for Android をコンパイルして Android スマートフォンにインストールします。
  • ステップ 4: 登録した Android スマートフォンからの GPS データを、Internet of Things Platform サービス・インスタンスにパブリッシュします。
  • ステップ 5: IBM Cloud アプリが IBM Watson IoT Platform 組織に接続するために必要な API キーを生成します。
1

オファー用データベースを作成する

まず始めに、ジオターゲティング・マーケティング・オファーを保管する MySQL データベースを作成します。このデータベースには、デバイスの最新の位置と、デバイスに配信済みのオファーのリストも保管します。PHP アプリケーションはオファーを検索する目的、そしてデバイスの位置を更新する目的の両方で、このデータベースに定期的に接続します。

このデータベースを作成するのに最も簡単な方法は、IBM Cloud ClearDB Managed MySQL Database サービスを利用することです。このサービスは、クラウド内でホストされる MySQL データベースをプロビジョニングします。デフォルトのプランでは、特定のクォータまでストレージを無料で利用できます。

  1. IBM Cloud アカウントにログインします。
  2. ダッシュボードで、「Create Service (サービスの作成)」をクリックします。
  3. 表示されるサービスのリストから「Data and Analytics (データとアナリティクス)」を選択し、「ClearDB Managed MySQL Database」サービスを選択します。
  4. 無料の CB5 プランを選択します。「Connect to (接続先)」フィールドには、データベース・インスタンスのバインド先として Internet of Things Platform サービス・インスタンスをホストする IBM Cloud アプリケーションがセットアップされるようにします。
    図 1. ClearDB サービス・インスタンスの作成
    ClearDB サービス・インスタンスを作成する画面のスクリーンショット
    ClearDB サービス・インスタンスを作成する画面のスクリーンショット
  5. データベース・インスタンスが初期化されたら、ClearDB ダッシュボードを開き、「Endpoint Information (エンドポイント情報)」タブをクリックしてインスタンスの資格情報を表示し、メモします。
    図 2. ClearDB サービス・インスタンスの資格情報
    ClearDB サービス・インスタンスの資格情報が示された画面のスクリーンショット
    ClearDB サービス・インスタンスの資格情報が示された画面のスクリーンショット

MySQL データベースのセットアップが完了したら、次は必要なテーブルを作成し、いくつかのマーケティング・オファーを使用して初期化する必要があります。

  1. 開発システム上の MySQL コマンド・ライン・クライアント (または他の MySQL 管理ツール) を使用して、MySQL インスタンスに接続し、以下の SQL コマンドを実行して必要なテーブルを作成します。
    CREATE TABLE `offer` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `latitude` decimal(10,8) NOT NULL,
      `longitude` decimal(11,8) NOT NULL,
      `message` text NOT NULL,
      PRIMARY KEY (`id`)
    );
    
    CREATE TABLE `device_offer` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `device_id` varchar(255) NOT NULL,
      `offer_id` int(11) NOT NULL,
      `offer_delivery_date` date NOT NULL,
      PRIMARY KEY (`id`)
    );
    
    CREATE TABLE `device_location` (
      `device_id` varchar(255) NOT NULL,
      `latitude` float(6,4) NOT NULL,
      `longitude` float(7,4) NOT NULL,
      `wait_time` int(11) NOT NULL,
      `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      PRIMARY KEY (`device_id`)
    );

    作成されるテーブルのそれぞれについて説明します。

    • offer テーブルは、マーケティング・オファーのリストを格納します。各オファーにはデータとして、メッセージとそのオファーを提供している位置の GPS 座標が含まれます。PHP アプリケーションはこの GPS データからオファーを識別し、登録済みデバイスに配信します。
    • device_location テーブルは、登録されている各デバイスから前回報告された GPS 座標と、その位置にデバイスが留まっている期間を格納します。このデータを使用して、オファーを送信する根拠となるだけの十分な期間、デバイスが 1 カ所に留まっているかどうかが計算されます。
    • device_offer テーブルは、登録されている各デバイスに配信されたオファーの日次リストを格納します。このデータを使用して、同じデバイスに同じオファーが 1 日に複数回配信されないようにします。
  2. まずは、以下のサンプル・データを offer テーブルのシード値として設定します。その後、自分の近くの位置が反映されるように、サンプル GPS 座標を更新してください (特定の位置の GPS 座標を取得するには、Google マップを利用できます)。
    INSERT INTO `offer` VALUES (51.5101195,-0.134737,'Get 20% off your pizza today!');
    INSERT INTO `offer` VALUES (51.5101195,-0.134737,'Buy one, get one free on event entry tonight!');
2

デバイス・データに対するトリガー・ルールとアクションを定義する

前のステップで、データベースを定義して、そのデータベースにデータを取り込むまでの作業が完了しました。次のステップでは、MySQL アプリケーションを使用して、Android スマートフォン (現時点で、IoT デバイスとしての役割も果たします) から IBM Watson IoT Platform に送信されるデータを MySQL データベースに取り込みます。データをデータベースに取りこむにはさまざまな方法がありますが、この記事では IBM Watson IoT Platform 内のトリガー・ルールとアクションによって PHP アプリケーションを起動してデータを処理する方法に焦点を絞ります。

前回の記事で説明した手順を完了していれば、Internet of Things Platform サービス・インスタンス内に Android デバイス・タイプが構成済みになっていて、Android デバイスが IBM Watson IoT Platform 組織に登録されており、デバイスからのデータ・ストリームが IBM Watson IoT Platform に送信されるようになっているはずです。このサンプル・アプリの場合、次のステップとなるのは、Android デバイス・タイプのスキーマを作成し、そのスキーマを使用してトリガー・ルールとアクションを構成することです。

  1. Android デバイス上で IoT Starter for Android アプリを開き、必須の資格情報を入力してから「Activate Sensor (センサーのアクティブ化)」をクリックします。これにより、アプリが IBM Watson IoT Platform に接続します。
  2. IBM Cloud 内で、デバイスのデータ・ストリームが Internet of Things Platform サービス・インスタンスのダッシュボードに表示されていることを確認します。
  3. Devices (デバイス)」メニューから「Manage Schemas (スキーマの管理)」セクションを選択し、「Add Schema (スキーマの追加)」ボタンをクリックします。
  4. 「Device Type (デバイス・タイプ)」画面で、選択項目のリストから「Android」を選択し、「Next (次へ)」をクリックします。
    図 3. デバイスのスキーマの作成
    デバイスのスキーマを作成する画面のスクリーンショット
    デバイスのスキーマを作成する画面のスクリーンショット
  5. 「Properties (プロパティー)」画面で、「Add a property (プロパティーを追加)」をクリックし、「From Connected (接続元)」タブを選択して、「d.latitude」、「d.longitude」、「d.timestamp」の 3 つのプロパティーを選択します。「OK」をクリックして、これらのプロパティーをスキーマに追加します。
    図 4. デバイスのスキーマの作成
    デバイスのスキーマを作成する画面のスクリーンショット
    デバイスのスキーマを作成する画面のスクリーンショット
  6. スキーマの定義が完了したので、「Rules (ルール)」メニューに進みます。「Browse (参照)」セクションで、「Create Cloud Rule (クラウド・ルールの作成)」ボタンをクリックします。
  7. ルールの名前 (例: offer) を入力し、先ほど作成した Android スキーマを選択します。「Next (次へ)」をクリックして続行します。
    図 5. ルールの作成
    ルールを作成する画面のスクリーンショット
    ルールを作成する画面のスクリーンショット
  8. 以下の手順に従って、トリガー・ルールと実行するアクションをクラウド・ルールに追加します。
    • 「If (条件)」セクションで「New condition (新しい条件)」をクリックし、選択ツールを使って条件「timestamp != 0」を追加します。「OK」をクリックして条件を保存します。
      図 6. 条件の定義
      条件を定義する画面のスクリーンショット
      条件を定義する画面のスクリーンショット
    • 「Trigger (トリガー)」セクションの「Trigger if conditions persist for (条件が次の期間存続した場合にトリガーする)」で、条件が 1 分間維持された場合にトリガーするようにルールを設定します。
      図 7. トリガー頻度の定義
      トリガー頻度を定義する画面のスクリーンショット
      トリガー頻度を定義する画面のスクリーンショット
    • 「Then (応答)」セクションで「New action (新しいアクション)」をクリックし、表示されるダイアログ・ボックスで「Add action (アクションを追加)」リンクをクリックします。事前定義された多数のアクションを使用できます。例えば、e-メールを送信する、アラートを表示する、IFTTT レシピをトリガーする、Node-RED の HTTP ノードまたは Webhook に接続するなどのアクションがあります。アクション・タイプとして「webhook action (Webhook アクション)」を選択してから、Webhook URL を http://my-iot-app-[イニシャル].mybluemix.net/webhook-offer.php に設定し、アクション・メソッドを「POST」に設定します。

      必ず、アクション URL に含まれるサンプル・ホスト名を IBM Cloud アプリケーションの実際のホスト名で置き換えてください。URL の末尾の PHP スクリプトについて気にする必要はありません。このスクリプトは現時点では存在しませんが、この記事の終わりには、このとおりの URL が存在することになります。

      他のフィールドはデフォルト値のままにします。リクエスト本体では、以下に示すように {{message}} プレースホルダー内にロー・デバイス・メッセージが含まれるようにしてください。「Finish (完了)」をクリックしてアクションを保存します。

      図 8. アクションの定義
      アクションを定義する画面のスクリーンショット
      アクションを定義する画面のスクリーンショット
  9. 「Rules (ルール)」 > 「Browse (参照)」を選択して新しいクラウド・ルールが表示されていることを確認し、「State (状態)」トグルをスライドしてルールをアクティブにします。
    図 9. ルールのアクティブ化
    ルールがアクティブにされている状態を示す画面のスクリーンショット
    ルールがアクティブにされている状態を示す画面のスクリーンショット

以上の構成手順を完了すると、デバイスがアクティブな状態で IBM Watson IoT Platform にデータを送信している限り、条件付きテストは常に true を返すようになります (デバイスのタイム・スタンプは常にゼロより大きい値であるためです)。したがって、Webhook アクションは構成されたトリガー頻度 (毎分) で実行され、デバイス・データを指定の URL に POST リクエストを使って送信します。

別の言い方をすると、このように構成されていれば、デバイスがアクティブな状態であり、IBM Watson IoT Platform に接続されている限り、デバイス・データを 1 分ごとに処理できるということです。

3

デバイス・データを処理してデバイス・コマンドをパブリッシュする

現時点で、ジオターゲティング・オファーを格納するデータベース、自身の GPS 座標を送信する Android スマートフォン (IoT デバイス)、デバイスからメッセージを受信している限り 1 分ごとに URL をリクエストするトリガーが揃いました。後は、これらのコンポーネントを PHP アプリケーションで結び付けて、PHP アプリケーションを指定の URL でホストすればよいだけです。

コードの詳細を探る前に、PHP による「結び付け」の要件を大まかに要約しましょう。

  • デバイスから送信される JSON データを受信してデコードする必要があります。
  • データベースをデバイスの現在地で更新するか、デバイスが移動していない場合は記録されている待機時間を更新する必要があります。
  • 一定の期間、デバイスが移動していない場合は、(a) デバイスを中心とした円の範囲内にあり、(b) 同日にまだデバイスに送信されていない、有効なオファーを検索する必要があります。
  • 上記の条件に一致するオファーのすべてを、IoT Starter for Android アプリケーションのコマンド・チャネル上で MQTT を使ってデバイスにプッシュする必要があります。

一見すると大量の作業のように思えますが、実際には思うほど難しい作業ではありません。以下に PHP アプリケーション・コードを記載します。このコードは開発ディレクトリー内に webhook-offer.php という名前で保存してください。

<?php
// include class
require('phpMQTT.php');

// load configuration values
$config = array(
  'org_id' => 'IOT-ORG-ID',
  'port' => '1883',
  'app_id' => 'phpmqtt',
  'iotp_api_key' => 'IOT-API-KEY',
  'iotp_api_secret' => 'IOT-API-TOKEN',
  'device_id' => 'DEVICE-ID',
  'qos' => 1  
  'db_host' => 'DATABASE-HOST-NAME',
  'db_user' => 'DATABASE-USER-NAME',
  'db_pass' => 'DATABASE-USER-PASSWORD',
  'db_name' => 'DATABASE-NAME',
  'wait_time_trigger_min' => 5,         
  'proximity_trigger_km' => 1,          
);
$config['server'] = $config['org_id'] . '.messaging.internetofthings.ibmcloud.com';
$config['client_id'] = 'a:' . $config['org_id'] . ':' . $config['app_id'];

// report MySQL errors as exceptions
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

// get JSON packet posted to webhook
$json = file_get_contents('php://input');

// decode JSON packet
// extract device ID, message contents
$data = json_decode($json);
$deviceId = $data->deviceId;
$message = json_decode($data->message);
$longitude = round($message->d->longitude, 4);
$latitude = round($message->d->latitude, 4);

// open database connection
$mysqli = new mysqli($config['db_host'], $config['db_user'], 
  $config['db_pass'], $config['db_name']);
if ($mysqli->connect_errno) {
  error_log("Failed to connect to MySQL: " . $mysqli->connect_error);
}

try {
  // check if this device already has a location record
  $sql = "SELECT latitude, longitude, wait_time FROM device_location 
    WHERE device_id = '$deviceId' LIMIT 0,1";
  $result = $mysqli->query($sql);
  $row = $result->fetch_object();
  // check if the last location recorded in the database matches the current location
  // if yes, update the wait time
  // if no, update the location record
  if ($result->num_rows == 1 && $row->latitude == $latitude 
    && $row->longitude == $longitude) {
    $wait_time = $row->wait_time + 1;
    $sql = "UPDATE device_location SET wait_time = '$wait_time', 
      updated = NOW() WHERE device_id = '$deviceId'";
    $mysqli->query($sql);
  } else {
    $sql = "DELETE FROM device_location WHERE device_id = '$deviceId'";
    $mysqli->query($sql);
    $wait_time = 0;
    $sql = "INSERT INTO device_location (device_id, latitude, longitude, 
      wait_time, updated) VALUES ('$deviceId', '$latitude', '$longitude', 
      '$wait_time', NOW())";
    $mysqli->query($sql);
  }
  
  // if the device has been in the same location
  // for the trigger number of minutes
  // find offers within the configured proximity radius
  // which have not already been delivered to the device today
  if ($wait_time == $config['wait_time_trigger_min']) {
    $proximity = $config['proximity_trigger_km'];
    $sql = "SELECT o.id, o.message, 
        (6371 * acos(cos(radians($latitude)) * cos(radians(latitude)) * 
        cos(radians(longitude) - radians($longitude)) + sin(radians($latitude)) * 
        sin(radians(latitude)))) AS distance
          FROM offer o
          LEFT JOIN device_offer dos ON o.id = dos.offer_id
        WHERE (dos.offer_delivery_date != DATE(NOW())
          OR dos.offer_delivery_date IS NULL)
        HAVING distance < $proximity
        ORDER BY distance
        LIMIT 0,10";
    $result = $mysqli->query($sql);

    if ($result->num_rows > 0) {
      // if offers found
      // initialize MQTT client
      $mqtt = new phpMQTT($config['server'], $config['port'], $config['client_id']); 
      $mqtt->debug = false;
      
      // connect to broker
      if(!$mqtt->connect(true, null, $config['iotp_api_key'], 
        $config['iotp_api_secret'])){
        error_log('Failed to Could not connect to IoT cloud');
        exit();
      } 

      // iterate over offer list and publish to device
      // update database with offer delivery status
      while ($row = $result->fetch_object()) {
        $offerId = $row->id;
        $message = $row->message;
        $mqtt->publish('iot-2/type/Android/id/' . $config['device_id'] . 
          '/cmd/alert/fmt/json', '{"d":{"text":"' . $message . '"}}', 1);      
        $sql = "INSERT INTO device_offer (device_id, offer_id, offer_delivery_date) 
          VALUES ('$deviceId', '$offerId', DATE(NOW()))";
        $mysqli->query($sql);
      }  

      // disconnect MQTT client
      $mqtt->close();      
    }    
  }
  
} catch (Exception $e) {
  error_log($e->getMessage());
}

// close database connection
$mysqli->close();

このアプリケーションでは phpMQTT ライブラリーという PHP クラスを使用して、MQTT メッセージおよびブローカーを処理します。この phpMQTT ライブラリーを PHP 開発環境に複製またはダウンロードして、PHP アプリケーションと同じディレクトリー内に配置します。

PHP アプリケーション・コードは、最初にこのライブラリーをロードして、先頭にある $config 配列内に必要な各種のパラメーターを設定します。指定されるパラメーターは以下のとおりです。

  • サーバー名。通常は、「messaging.internetofthings.ibmcloud.com」ドメイン内にある組織 ID にちなんだ名前が付けられます。
  • サーバー・ポート。非暗号化接続の場合は 1883、暗号化接続の場合は 8883 です。
  • クライアント ID。「a:ORG_ID:APP_ID」の形式で指定されます。APP_ID はユーザーが指定する値です。
  • クライアントの API キーとシークレット。これらはサービスにアクセスするために必要です。
  • データベースのホスト名、アクセス資格情報、データベース名。これらの値は、前に説明したように ClearDB ダッシュボードから取得できます。
  • デバイスが静止状態にあると判断するまでの分数 (デフォルトでは 5 分に設定) と、デバイス位置を中心とした、有効なオファーを調べる地理的範囲 (デフォルトでは半径 1 km に設定)。

構成の要点を説明したところで、次はアプリケーション・コードを詳しく見ていきましょう。

  1. このアプリケーションは、前のステップでセットアップしたクラウド・ルールによって 1 分ごとに呼び出され、デバイスからのロー・データ (デバイスの GPS 座標など) が含まれる JSON パケットが渡されます。したがって、この PHP アプリケーションが第一に行う仕事は、この JSON パケットを読み取ってデコードし、そこに含まれる重要な情報を PHP 変数に割り当てることです。
    <?php
    // ...
    // get JSON packet posted to webhook
    $json = file_get_contents('php://input');
    $data = json_decode($json);
    $deviceId = $data->deviceId;
    $message = json_decode($data->message);
    $longitude = round($message->d->longitude, 4);
    $latitude = round($message->d->latitude, 4);
    // ...
  2. 次に、アプリケーションは MySQL データベースへの接続を開きます。そして「device_location」テーブルを調べて、呼び出し元のデバイスの位置レコードがすでに存在するかどうかを判別します。存在する場合、レコードが最後に更新されてからデバイスの位置が変わったかどうかを判別します。位置が変わっている場合は位置レコードを更新し、同じ位置に留まっている場合は待機時間を 1 分増やして更新します。

    「device_location」テーブル内の latitude/longitude フィールドの値は、「offer」テーブル内での値よりも粒度が粗いことも注意してください。この設計は、エラーの許容範囲を設けるために意図的に行われています。このようにしなければ、デバイスのわずかな移動 (例えば、店舗内のある場所から別の場所への移動) でも、新しい位置座標が生成されて、待機タイマーがリセットされてしまいます。

    <?php
    // ...
    // check if this device already has a location record
    $sql = "SELECT latitude, longitude, wait_time FROM device_location 
      WHERE device_id = '$deviceId' LIMIT 0,1";
    $result = $mysqli->query($sql);
    $row = $result->fetch_object();
    // check if the last location recorded in the database matches the current location
    // if yes, update the wait time
    // if no, update the location record
    if ($result->num_rows == 1 && $row->latitude == $latitude 
      && $row->longitude == $longitude) {
      $wait_time = $row->wait_time + 1;
      $sql = "UPDATE device_location SET wait_time = '$wait_time', updated = NOW() 
        WHERE device_id = '$deviceId'";
      $mysqli->query($sql);
    } else {
      $sql = "DELETE FROM device_location WHERE device_id = '$deviceId'";
      $mysqli->query($sql);
      $wait_time = 0;
      $sql = "INSERT INTO device_location (device_id, latitude, longitude, 
        wait_time, updated) VALUES ('$deviceId', '$latitude', '$longitude', 
        '$wait_time', NOW())";
      $mysqli->query($sql);
    }
    // ...
  3. 構成された期間にわたってデバイスが静止している場合、次のステップとなるのは、デバイスが位置する地域内で有効なオファーを見つけることです。この設計を実現するには、デバイスの現在地を中心として円を「描き」、その円の範囲内で一致するオファーを探します。この計算はやや複雑ですが、補足記事にリンクが張られている Google マップの資料に、対応する SQL クエリーと、式の数学表現および説明へのリンクが記載されています。

    特定の地理的範囲内で有効なオファーを見つけることとは別に、この PHP アプリケーション内の SQL クエリーは、有効なオファーを「device_offer」テーブルに照合して、同日にすでにデバイスに配信されたオファーではないことを確認する必要があります。この設計を実現するには、「offer」テーブルに「device_offer」テーブルを結合し、結果セットのフィルタリング基準を SQL クエリーに追加して、構成済みの地理的範囲内にあり、同時にまだデバイスにパブリッシュされていないオファーだけがクエリーから返されるようにします。以下に、このクエリーを記載します。

    SELECT o.id, o.message, 
      (6371 * acos(cos(radians($latitude)) * cos(radians(latitude)) * 
        cos(radians(longitude) - radians($longitude)) + sin(radians($latitude)) * 
        sin(radians(latitude)))) AS distance
        FROM offer o
        LEFT JOIN device_offer dos ON o.id = dos.offer_id
      WHERE (dos.offer_delivery_date != DATE(NOW())
        OR dos.offer_delivery_date IS NULL)
      HAVING distance < $proximity
      ORDER BY distance
      LIMIT 0,10
  4. SQL クエリーから 1 つ以上の一致結果が返された場合、PHP アプリケーションは MQTT を使用して、それらのオファーをデバイスにパブリッシュする必要があります。そのために、PHP アプリケーションは新しい phpMQTT オブジェクトを初期化し、このオブジェクトの connect() メソッドと構成済みの API キーおよび認証トークンを使用して IBM Watson IoT Platform に接続します。接続した後、このオブジェクトの publish() メソッドを使用してオファー・メッセージをデバイスの iot-2/cmd/alert/fmt/json トピックに送信します。このコマンド・トピックにパブリッシュされるメッセージは、デバイス上の IoT Starter for Android アプリケーションによって自動的にアラートに変換されます。
    <?php
    // ...
    $mqtt = new phpMQTT($config['server'], $config['port'], $config['client_id']); 
    $mqtt->debug = false;
    
    // connect to broker
    if(!$mqtt->connect(true, null, $config['iotp_api_key'], 
      $config['iotp_api_secret'])){
      error_log('Failed to Could not connect to IoT cloud');
      exit();
    } 
    
    // iterate over offer list and publish to device
    // update database with offer delivery status
    while ($row = $result->fetch_object()) {
      $offerId = $row->id;
      $message = $row->message;
      $mqtt->publish('iot-2/type/Android/id/' . $deviceId . 
        '/cmd/alert/fmt/json', '{"d":{"text":"' . $message . '"}}', 1);      
      $sql = "INSERT INTO device_offer (device_id, offer_id, offer_delivery_date) 
        VALUES ('$deviceId', '$offerId', DATE(NOW()))";
      $mysqli->query($sql);
    }  
    
    // disconnect MQTT client
    $mqtt->close();   
    // ...
  5. PHP アプリケーションはメッセージをデバイスにパブリッシュした後、「device_offer」テーブルを更新するため、IoT Starter for Android アプリケーションが同日に再びトリガーされても、同じオファーはユーザーに再送信されません。

    以下に、IoT Starter for Android アプリケーションが PHP アプリケーションから受信するオファーの例を示します。

    図 10. デバイス通知の例
    デバイス通知としてのオファーの一例を示す画面のスクリーンショット
    デバイス通知としてのオファーの一例を示す画面のスクリーンショット
4

PHP アプリケーションを IBM Cloud にデプロイする

最後のステップとして、PHP アプリケーションを IBM Cloud にデプロイします。

  1. アプリケーションのマニフェストを作成します。ホスト名とアプリケーションには必ず、ClearDB Managed MySQL Database サービスと Internet of Things Platform サービスがバインドされた既存の IBM Cloud アプリケーションのホスト名とアプリケーション名と一致する名前を使用してください。
    ---
    applications:
    - name: my-iot-app-[initials]
    memory: 256M
    instances: 1
    host: my-iot-app-[initials]
    buildpack: https://github.com/cloudfoundry/php-buildpack.git
    stack: cflinuxfs2
  2. PHP 用 MySQLi 拡張を使用するようにビルドパックを構成する必要もあります。それには、アプリケーション・ディレクトリー内に bp-config/php/php.ini.d/php.ini ファイルを作成し、そのファイルに以下の内容を含めます。
    extension=mysqli.so
  3. 以下のコマンドを使用してアプリケーションを IBM Cloud にプッシュします。
    shell> cf api https://api.ng.bluemix.net
    shell> cf login
    shell> cf push

これで、http://my-iot-app-[initials].mybluemix.net/webhook-offer.php にあるアプリケーションを閲覧できるようになったはずです。アプリケーションを閲覧しているのであって、JSON データは送信していないため、エラー・メッセージが表示されますが、それでも URL がアクティブになり、IBM Watson IoT Platform からの POST データを受信できる状態であるという確認にはなります。何も表示されない場合は、デバッグ・ログをどのようにして入手するのかについて、補足記事に記載されているリンク先のページを参照してください。

まとめ

この記事で明らかにしたように、IBM Watson IoT Platform を利用すると、接続されたデバイスからデータを受信できるだけでなく、接続されたデバイスにデータをパブリッシュすることもできます (しかもそのデータを使用して、計算、アラートの表示など、さまざまな興味深いことを実行できます)。それだけではありません。IBM Watson IoT Platform では、デバイス・データに対するトリガー・ルールとアクションを構成して、アラートを送信したり、レシピをトリガーしたり、リモート・アプリケーション URL をリクエストするなどといったことも可能です。これらの機能を IBM Cloud 内で利用できる幅広いサービスと組み合わせれば、IBM Cloud と Watson IoT Platform の両方から最強の機能を統合した高度でスケーラブルな IoT アプリケーションを構築する無限の可能性が見えてくるはずです。

このアプリケーションを IBM Cloud と IBM Watson IoT Platform 上で試してみたい場合は、まず、GitHub からソース・コードをダウンロードして、この記事で説明した内容を参考にコードの詳細を調べてください。その上で、環境に合わせて構成を変更して Android デバイスと組み合わせてデプロイすれば、アプリケーションがどのように機能するのかを確認できます。どうぞ、お楽しみください!


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source, Web development, Mobile development
ArticleID=1052211
ArticleTitle=位置認識機能を備えた IoT アプリを作成する、第 2 回: ジオターゲティング広告を IoT デバイスに配信する PHP アプリを作成する
publish-date=11242017