AWS IoT の Device Shadow を iOS アプリから MQTT で使ってみた #reinvent

2015.10.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

AWS re:Invent 2015 で発表された AWS IoT は IoT (モノのインターネット) のためのマネージド型のクラウドプラットフォームを提供してくれるサービスです。現在はベータ版として一般公開されています(2015/10/26現在)。

AWS IoT の機能の一つである「Device Shadow」を使うと、IoT デバイスの状態を管理することができます。IoT デバイスのセンサー情報を受け取ったり、逆に IoT デバイスの状態を変更するためにデータを送ったり、双方向にやり取りすることができます。この通信には MQTT または HTTPS が利用できます。

AWS IoT を試したい!しかしながら「IoT デバイスは持っていない」「コマンドラインは苦手…」という人も少なくないと思います。そこで、この記事では「AWS IoT の Device Shadow を iOS アプリから使ってみる」ということを試してみたいと思います。

Mac と Xcode、そしてインターネットがあれば、誰でもできます!

Thing の作成

まずは iPhone を Thing として AWS IoT に登録しましょう。マネジメントコンソールから「Create a thing」を選び、名前を「iPhone-5s」とします。

iot-mqtt-01

View thing」を押すと、エンドポイントや Device Shadow 用の MQTT トピックなどが確認できます。これらはあとで使います。

iot-mqtt-02

次にポリシーと証明書を作成し、証明書をダウンロードします。「Connect a Device」をクリックしましょう。

「Embedded C」を選択し、証明書を生成します。作成が終わると Public キー、Private キー、証明書ファイルがダウンロード可能となります。今回は Private キー、証明書ファイルをダウンロードしておきます。

iot-mqtt-03

これで AWS IoT 側の準備は終わりです。なお、ここまでの操作はコマンドラインからも行えます。コマンドラインから行う方法は、次の記事を参考にしてください。

iOS アプリの実装

次に、iOS アプリを実装しましょう。このアプリでは、現在の位置情報を MQTT トピックに Publish してみたいと思います。なお、今回は Swift で書きます。

iOS 向け MQTT クライアントライブラリについて

iOS で利用可能な MQTT クライアントライブラリはいくつかありますが、ほぼ mosquito のラッパーです。その中から今回は Moscapsule を使用しました。こちらを使うと X.509 形式の証明書を利用した MQTT 通信が行えます。OpenSSL-Universal も依存しているので、こちらも一緒にインポートします。

また、Device Shadow に JSON データを送る必要があるため、JSON を簡単に扱うため SwiftyJSON もインポートします。

use_frameworks!

target 'MQTTSample' do
  pod 'Moscapsule', :git => 'https://github.com/flightonary/Moscapsule.git'
  pod 'OpenSSL-Universal', '~> 1.0.1.l'
  pod 'SwiftyJSON'
end

証明書のインポート

次に、Xcode プロジェクトに先ほどダウンロードしてきた証明書と Private キーファイルをインポートします。また、それらに加えて ルートCA ファイルも必要です。ルートCA ファイルはシマンテックのサイトからダウンロードしてインポートしておきます。ここでは ca.pem という名前にしています。

iot-mqtt-05

ちなみに Xcode では証明書ファイルの情報を閲覧することができます。AWS から発行されていること、そして2050年まで有効であることが確認できます。

iot-mqtt-06

Device Shadow の Topic に向けた Publish

ということで実装です。まずは View Controller に CoreLocation を使った位置情報の取得を実装し、locationManager:didUpdateLocations: メソッドで取得できた位置情報を Device Shadow に向けて Publish します。位置情報を利用するときは Info.plistNSLocationWhenInUseUsageDescription を登録しないといけないので、忘れずに追加してください。

import UIKit
import Moscapsule
import SwiftyJSON
import CoreLocation

class ViewController: UIViewController, CLLocationManagerDelegate {

let manager = CLLocationManager()
var mqttClient: MQTTClient?

override func viewDidLoad() {
super.viewDidLoad()
initMQTTClient()
self.manager.delegate = self
self.manager.requestWhenInUseAuthorization()
}

func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedWhenInUse {
manager.startUpdatingLocation()
}
}

// 位置情報の更新で MQTT のトピックに送信
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.first!
let json: JSON = [
"state": [
"reported": [
"location": [
"latitude": location.coordinate.latitude,
"longitude": location.coordinate.longitude
]
]
]
]
publishTopic(json)
}

// MQTT クライアントの初期設定
func initMQTTClient() {
moscapsule_init()
let mqttConfig = MQTTConfig(clientId: "server_cert_test",
host: "XXXXXXXXXXXXXX.iot.ap-northeast-1.amazonaws.com", port: 8883, keepAlive: 60)
let certFile = NSBundle.mainBundle().pathForResource("xxxxxxxxxx-certificate.pem", ofType: "crt")
let keyFile = NSBundle.mainBundle().pathForResource("xxxxxxxxxx-private.pem", ofType: "key")
let caFile = NSBundle.mainBundle().pathForResource("ca", ofType: "pem")
mqttConfig.mqttServerCert = MQTTServerCert(cafile: caFile, capath: nil)
mqttConfig.mqttClientCert = MQTTClientCert(certfile: certFile!, keyfile: keyFile!, keyfile_passwd: nil)
self.mqttClient = MQTT.newConnection(mqttConfig)
}

// Device Shadow トピックに向けた Publish
func publishTopic(json: JSON) {
let data = try! json.rawData()
self.mqttClient!.publish(data, topic: "$aws/things/iPhone-5s/shadow/update", qos: 2, retain: false)
}

}

特記すべきは initMQTTClient メソッドの中身です。MQTT の設定は MQTTConfig クラスで行いますが、証明書やら Private キーやらを適切に設定してあげる必要があります。

MQTTServerCert にはルート CA ファイルと呼ばれる証明書を設定する必要があります。これは先ほどシマンテックからダウンロードした ca.pem ですね。capathnil で結構です。

MQTTClientCert には証明書ファイルと Private キーファイルを設定します。証明書ファイルは AWS IoT のマネジメントコンソールからダウンロードしてきた xxxxxxxxxx-certificate.pem という形式のファイル、Private キーのファイルは xxxxxxxxxx-private.pem という形式のファイルをそれぞれ設定します。

試してみる

アプリを実行してみましょう。次のようなログがコンソールで出れば Publish できています。

2015-10-26 00:31:49.878 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG   Client server_cert_test sending CONNECT
2015-10-26 00:31:50.262 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG   Client server_cert_test received CONNACK
2015-10-26 00:32:50.261 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG   Client server_cert_test sending PINGREQ
2015-10-26 00:32:50.262 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG   Client server_cert_test sending PUBLISH (d1, q2, r0, m1, '$aws/things/iPhone-5s/shadow/update', ... (86 bytes))
2015-10-26 00:32:50.262 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG   Client server_cert_test sending PUBLISH (d1, q2, r0, m2, '$aws/things/iPhone-5s/shadow/update', ... (86 bytes))
2015-10-26 00:32:50.262 MQTTSample[23715:1595292] [MOSQUITTO] DEBUG   Client server_cert_test sending PUBLISH (d1, q2, r0, m3, '$aws/things/iPhone-5s/shadow/update', ... (86 bytes))

...

AWS IoT で様子を見てみます。

iot-mqtt-07

バッチリ送信できました!

まとめ

気軽に試したい一心で、iOS アプリから試してみました。Android アプリでやる場合は Paho などを使うと良さそうです。

この記事のアイデアは こちらのセッション から来ているものですが、IoT デバイスを遠隔操作したり、遠隔で状態を確認したりするときに使えると思います。また、IoT デバイスを気軽にシミュレートする用途でもお使い頂けると思います。

自分の iPhone から、IoT を体験してみましょう!参考になれば幸いです。

参考