IoT Amazon MQTT クライアント

AWS IoT とは何ですか?

AWS IoT は、センサー、アクチュエーター、組み込みマイクロコントローラー、スマートアプライアンスなどのインターネット接続デバイスと AWS クラウドとの間に安全な双方向通信を提供します。これにより、複数のデバイスからテレメトリデータを収集してデータを保存および分析することができます。また、ユーザーが電話やタブレットからこれらのデバイスを制御できるアプリケーションを作成することもできます。

 

Message broker

デバイスと AWS IoT アプリケーションが互いにメッセージを送受信するための安全なメカニズムを提供します。MQTT プロトコルを直接使用することも、WebSocket 経由の MQTT を使用してパブリッシュとサブスクライブを行うこともできます。

 

AWS IoT メッセージブローカーは、AWS IoT との間でメッセージを送受信できるパブリッシュ/サブスクライブブローカーサービスです。AWS IoT と通信する場合、クライアントは Sensor/temp/room1 のようなトピックに宛てたメッセージを送信します。

 

メッセージブローカーは次に、そのトピックのメッセージを受信するために登録したすべてのクライアントにメッセージを送信します。メッセージを送信する行為をパブリッシングと呼びます。トピックフィルターのメッセージを受信するために登録する行為をサブスクライビングと呼びます。

 

トピック名前空間は AWS アカウントとリージョンのペアごとに分離されています。たとえば、AWS アカウントの Sensor/temp/room1 トピックは別の AWS アカウントの Sensor/temp/room1 トピックとは独立しています。リージョンも同様です。同じ AWS アカウントで us-east-1 の Sensor/temp/room1 トピックは us-east-2 の同じトピックとは独立しています。AWS IoT は AWS アカウントやリージョンをまたいだメッセージの送受信をサポートしていません。

 

メッセージブローカーは、すべてのクライアントセッションと各セッションのサブスクリプションのリストを維持します。トピックにメッセージが発行されると、ブローカーはそのトピックにマッピングされるサブスクリプションを持つセッションを確認します。次にブローカーは、現在接続中のクライアントを持つすべてのセッションに発行メッセージを転送します。

 

MQTT クライアント

TsgcIoTAmazon_MQTT_Client はAWS IoTへの接続に使用するコンポーネントです。1つのクライアントは1つのデバイスにのみ接続できます。クライアントはプレーンMQTTプロトコルを使用して接続し、X.509クライアント証明書を使用して認証します。

 

AWS IoT に接続するには、クライアントに以下のプロパティが必要です:

 

Amazon.ClientId: クライアントの識別子。オプションです。

Amazon.Endpoint: MQTT クライアントが接続するサーバー名。

Amazon.Port: デフォルトではポート8883を使用します。ポートが443の場合、接続にALPNを自動的に使用します(カスタムIndyバージョンが必要)。

 

AWS IoT Core は MQTT および WebSocket Secure (WSS) プロトコルを使用してメッセージをパブリッシュおよびサブスクライブするデバイスとクライアントをサポートしています。次の表は AWS IoT デバイスエンドポイントがサポートするプロトコルと使用する認証メソッドおよびポートを示しています。

 

プロトコル 認証 Port ALPN プロトコル名
MQTT over WebSocket 署名バージョン4 443  
MQTT over WebSocket カスタム認証 443  
MQTT X.509クライアント証明書 443 x-amzn-mqtt-ca
MQTT X.509クライアント証明書 8883  
MQTT カスタム認証 443 mqtt

 

 

証明書による認証

Amazon AWS コンソールで証明書を作成し、証明書が保存されているパスを設定する必要があります。

 

IOHandler として OpenSSL を使用する場合、以下のパスに証明書を設定する必要があります

 

Certificate.Enabled: 証明書を使用する場合は True に設定します。

Certificate.CertFile:X.509クライアント証明書へのパス。

Certificate.KeyFile: X.509クライアントキーファイルへのパスです。

 

SChannel を IOHandler として使用する場合、まず PEM 証明書とキーを PFX 証明書に変換します。これには OpenSSL バイナリが必要です。

 


openssl pkcs12 -inkey 884ccf73ff-private.pem.key -in 884ccf73ff-certificate.pem.crt -export -out 884ccf73ff-certificate.pfx

次に以下のパスを設定してください(証明書にはすでにキーファイルが含まれているため、キーファイルを設定する必要はありません)。

 

Certificate.Enabled: 証明書を使用する場合は True に設定します。

Certificate.CertFile: PFX証明書へのパス

 

SignatureV4 Authentication

Amazon AWS コンソールでユーザーを作成し、WebSocket リクエストの署名に使用するアクセスキーとシークレットキーを保存する必要があります。

 

SignatureV4.Enabled: この認証タイプを使用する場合は True に設定します。

SignatureV4.Region: デバイスが配置されているリージョン(例:us-east-1)。

SignatureV4.AccessKey: Amazon コンソールで作成されたか、一時的な認証情報として取得されたアクセスキー。

SignatureV4.SecretKey: Amazon コンソールで作成またはテンポラリー認証情報として取得したシークレットキー。

SignatureV4.SessionToken:(条件付き)一時セキュリティ認証情報を使用している場合は、ここにセキュリティトークンを設定します。

OpenSSL_Options: OpenSSL ライブラリの設定。

APIVersion: 使用する OpenSSL API を定義できます。

oslAPI_1_0: API 1.0 OpenSSL を使用します。Indy で最後にサポートされたバージョンです。

oslAPI_1_1: API 1.1のOpenSSLを使用します。カスタムIndyライブラリが必要で、OpenSSL 1.1.1ライブラリ(TLS 1.3サポート付き)の使用が可能になります。

oslAPI_3_0: API 3.0 OpenSSLを使用します。当社のカスタムIndyライブラリが必要であり、OpenSSL 3.0.0ライブラリの使用を可能にします(TLS 1.3サポート付き)。

LibPath: openSSLライブラリの場所を設定できます

oslpNone: これはデフォルトです。openSSLライブラリは、バイナリと同じフォルダー、または既知のパスに ある必要があります。

oslpDefaultFolder: すべてのIDEパーソナリティに対してライブラリが配置されるべきopenSSLパスを自動的に設定します。

oslpCustomFolder: このオプションが選択されている場合、プロパティ LibPathCustom にフルパスを定義します。

LibPathCustom: LibPath = oslpCustomFolder の場合、openSSL ライブラリが格納されているフォルダーの完全パスをここで定義します。

UnixSymLinks: Unix システムでシンボリックリンクの読み込みを有効または無効にします(デフォルトでは有効です。OSX64 は除きます)。

oslsSymLinksDefault: デフォルトでは有効です。ただし OSX64 では無効です(MacOS Monterey 以降、バージョンなしでライブラリを読み込もうとすると失敗するため)。

oslsSymLinksLoadFirst: SymLinksを読み込み、バージョンライブラリを読み込む前に行います。

oslsSymLinksLoad: バージョンライブラリの読み込みを試みた後にシンボリックリンクを読み込みます。

oslsSymLinksDontLoad: SymLinksを読み込みません。

 

*SignatureV4 には Indy 10.5.7 以降が必要です

カスタム認証

カスタム認証を使用すると、オーソライザーリソースを使用してクライアントの認証と認可方法を定義できます。デバイスは、リクエストのヘッダーフィールドまたはクエリパラメーター(WebSocket 上の MQTT プロトコルの場合)、あるいは MQTT CONNECT メッセージのユーザー名とパスワードフィールド(MQTT および WebSocket 上の MQTT プロトコルの場合)で認証情報を渡します。

 

CustomAuthentication.Enabled: この種類の認証を使用する場合は True に設定します。

CustomAuthentication.Parameters: サーバーに渡されるクエリ パラメータをここで設定します(デフォルトは /mqtt)

CustomAuthentication.Headers: カスタムヘッダーフィールドをここに設定できます。

CustomAuthentication.WebSockets: trueに設定すると、接続はWebSocketプロトコルで動作します。それ以外の場合はプレーンTCPで動作します。

 

MQTTAuthentication.Enabled: MQTT 接続でユーザー名/パスワードを渡す必要がある場合は、このプロパティを有効にします。

MQTTAuthentication.Username: mqtt接続のユーザー名です

MQTTAuthentication.Password: MQTT 接続のシークレット。

 

 

クライアントはオプションで ClientId を送信してクライアント接続を識別できます。他のクライアントはこのクライアントが接続、サブスクライブ、切断するたびに通知を受け取るためにサブスクライブできます。

 

認証

ポート 8883 を使用して TCP をトランスポートとして接続できない場合(デフォルト)、Amazon は「AWS IoT Core ポリシー」を使用してクライアントとサブスクリプションに認証を提供または拒否します。おそらくクライアント ID を認可する必要があります。

Amazon AWS コンソールにログインし、IoT Core に移動して「Secure/Policies」メニューにアクセスし、IoT Thing にアタッチされているポリシーを選択して、最後に接続がどのように設定されているか確認してください。例:

 

{

"Effect": "Allow",

"Action": [

"iot:Connect"

],

"Resource": [

"arn:aws:iot:us-east-1:222178873557:client/sdk-java",

"arn:aws:iot:us-east-1:222178873557:client/basicPubSub",

"arn:aws:iot:us-east-1:222178873557:client/sdk-nodejs-*"

]

}

 

この設定は、ID が sdk-java、basicPubSub、sdk-nodejs-* のクライアントのみが接続できることを意味します。必要に応じて変更して再試行してください。

それでも動作しない場合は、ログを有効にしてCloudWatchで接続できない理由を確認します。

 

その他のプロパティ

 

MQTTHeartBeat: 有効にすると、X秒ごとにpingを送信してMQTT接続を維持しようとします。

 

Interval: 各 ping 間の秒数。

 

MQTTAuthentication: 有効にすると MQTT 接続にユーザー名とパスワードを含めます。

 

UserName: ユーザーの名前

パスワード: シークレット文字列

 

WatchDog: 有効にすると、予期しない切断が検出されたときに、自動的にサーバーへの再接続を試みます。

 

Interval: 再接続試行前の秒数。

 

Attempts: 再接続試行の最大回数。ゼロは無制限を意味します。

 

LogFile: 有効にすると、ソケットメッセージをログファイルに保存します(デバッグに便利)。複数のスレッドからアクセスされる場合、ログファイルへのアクセスはスレッドセーフではありません。

 

Enabled: 有効にすると、ソケットでメッセージが受信・送信されるたびにファイルに保存されます。

 

FileName: ファイル名への完全パス。

 

Implementation

 

Amazon の MQTT 実装は MQTT バージョン 3.1.1 をベースにしていますが、仕様から以下の点で異なります:

 

 

AWS IoT への接続

まず、AWS コンソールにサインインし、新しいデバイスを登録してデバイス用の X.509 証明書を作成します。完了したら、新しい TsgcIoTAmazon_MQTT_Client を作成して AWS IoT サーバーに接続できます。例:

 


oClient := TsgcIoTAmazon_MQTT_Client.Create(nil);
oClient.Amazon.Endpoint := 'a2ohgdjqitsmij-ats.iot.us-west-2.amazonaws.com';
oClient.Amazon.ClientId := 'sgcWebSockets';
oClient.Certificate.CertFile := 'amazon-certificate.pem.crt';
oClient.Certificate.KeyFile := 'amazon-private.pem.key';
oClient.OnMQTTConnect := OnMQTTConnectEvent;
oClient.Active := True;
 
procedure OnMQTTConnect(Connection: TsgcWSConnection; const Session: Boolean; const ReturnCode: TmqttConnReturnCode);
begin
  ShowMessage('Connected to AWS');
end;

トピック

メッセージブローカーはトピックを使用してパブリッシングクライアントからサブスクライビングクライアントにメッセージをルーティングします。スラッシュ(/)はトピック階層を区切るために使用されます。次の表は購読時のトピックフィルターで使用できるワイルドカードの一覧です。# は購読するトピックの最後の文字である必要があります。現在のツリーとすべてのサブツリーに一致するワイルドカードとして機能します。

たとえば、Sensor/# へのサブスクリプションは Sensor/、Sensor/temp、Sensor/temp/room1 に発行されたメッセージを受信しますが、Sensor に発行されたメッセージは受信しません。

+ トピック階層内の 1 つのアイテムに正確にマッチします。例えば、Sensor/+/room1 へのサブスクリプションは Sensor/temp/room1、Sensor/moisture/room1 などに発行されたメッセージを受信します。

 


oClient := TsgcIoTAmazon_MQTT_Client.Create(nil);
...
oClient.OnSubscribe := OnSubscribeEvent;
 
vPacketIdentifier := oClient.Subscribe('Sensor/moisture/room1');
  
procedure OnMQTTSubscribe(Connection: TsgcWSConnection; aPacketIdentifier: Word; aCodes: TsgcWSSUBACKS);
begin
  if vPacketIdentifier = aPacketIdentifier then
    ShowMessage('Subscribed to topic Sensor/moisture/room1'); 
end;
 
// Client, can send a message using Publish method.
oClient.Publish('Sensor/moisture/room1', '{"temp"=10}');
  
// Messages received from server, are dispatched OnMQTTPublishEvent.
// For extended payload access (string, bytes or stream), use OnMQTTPublishEx.
procedure OnMQTTPublish(Connection: TsgcWSConnection; aTopic, aText: string);
begin
  DoLog('Received Message: ' + aTopic + ' ' + aText);
end;

Reserved Topics

以下のメソッドは予約済みトピックへのサブスクライブ/公開に使用されます。

 

Subscribe_ClientConnected(const aClientId: String): 指定したクライアント ID を持つ MQTT クライアントが AWS IoT に接続すると、AWS IoT はこのトピックにパブリッシュします

Subscribe_ClientDisconnected(const aClientId: String): 指定したクライアントIDを持つMQTTクライアントがAWS IoTから切断すると、AWS IoTがこのトピックに発行します

Subscribe_ClientSubscribed(const aClientId: String): 指定したクライアントIDを持つMQTTクライアントがMQTTトピックにサブスクライブすると、AWS IoTがこのトピックに発行します

Subscribe_ClientUnSubscribed(const aClientId: String): 指定されたクライアント ID を持つ MQTT クライアントが MQTT トピックからサブスクライブ解除したときに、AWS IoT がこのトピックにパブリッシュします。

 

Publish_Rule(const aRuleName, aText: String): デバイスまたはアプリケーションがこのトピックに公開することで、ルールを直接トリガーします。

 

Publish_DeleteShadow(const aThingName, aText: String): デバイスまたはアプリケーションがシャドウを削除するためにこのトピックに公開します。

Subscribe_DeleteShadow(const aThingName: String): デバイスまたはアプリケーションがシャドウを削除するためにこのトピックにサブスクライブします

Subscribe_ShadowDeleted(const aThingName: String): シャドウが削除されたときに Device Shadow サービスがこのトピックにメッセージを送信します

Subscribe_ShadowRejected(const aThingName: String): シャドウの削除リクエストが拒否された場合、Device Shadow サービスはこのトピックにメッセージを送信します。

Publish_ShadowGet(const aThingName, aText: String): アプリケーションまたはシャドウを取得するためにこのトピックに空のメッセージをパブリッシュします

Subscribe_ShadowGet(const aThingName: String): アプリケーションまたはシングルがシャドウを取得するためにこのトピックにサブスクライブします。

Subscribe_ShadowGetAccepted(const aThingName: String): Device Shadowサービスは、shadowのリクエストが正常に行われたときにこのトピックにメッセージを送信します

Subscribe_ShadowGetRejected(const aThingName: String): Device Shadow サービスがシャドウへのリクエストを拒否したときにこのトピックにメッセージを送信します

Publish_ShadowUpdate(const aThingName, aText: String): thingまたはアプリケーションがこのトピックにパブリッシュしてシャドウを更新します

Subscribe_ShadowUpdateAccepted(const aThingName: String): Device Shadow サービスは、シャドウへの更新が正常に行われたときにこのトピックにメッセージを送信します

Subscribe_ShadowUpdateRejected(const aThingName: String): Device Shadowサービスは、shadowへの更新が拒否されたときにこのトピックにメッセージを送信します

Subscribe_ShadowUpdateDelta(const aThingName: String): デバイスシャドウサービスは、シャドウの reported セクションと desired セクションの間に差分が検出されたとき、このトピックにメッセージを送信します

Subscribe_ShadowUpdateDocuments(const aThingName: String): シャドウへの更新が正常に実行されるたびに、AWS IoT はこのトピックに状態ドキュメントをパブリッシュします

 

永続セッション

永続的なセッションは MQTT メッセージブローカーへの継続的な接続を表します。クライアントが永続的なセッションを使用して AWS IoT メッセージブローカーに接続すると、メッセージブローカーは接続中にクライアントが行うすべてのサブスクリプションを保存します。クライアントが切断すると、メッセージブローカーは未確認の QoS 1 メッセージと、クライアントがサブスクライブしているトピックに発行された新しい QoS 1 メッセージを保存します。クライアントが永続的なセッションに再接続すると、すべてのサブスクリプションが復元され、保存されたすべてのメッセージが最大秒速10メッセージの速度でクライアントに送信されます。

 

OnMQTTBeforeConnect イベントで cleanSession パラメータを False に設定することで、MQTT 永続セッションを作成します。クライアントのセッションが存在しない場合は、新しい永続セッションが作成されます。セッションが既に存在する場合は再開されます。

 

デバイスは OnMQTTConnect イベントの Session 属性を確認して、永続セッションが存在するかどうかを判断する必要があります。Session が True の場合、永続セッションが存在し、保存されたメッセージがクライアントに配信されます。Session が False の場合、永続セッションは存在せず、クライアントはトピックフィルターに再サブスクライブする必要があります。

 

永続セッションのデフォルトの有効期限は1時間です。有効期限はメッセージブローカーがクライアントの切断 (MQTT 切断またはタイムアウト) を検出した時点から始まります。永続セッションの有効期限は標準の制限引き上げプロセスを通じて延長できます。クライアントが有効期限内にセッションを再開しなかった場合、セッションは終了し、関連する保存済みメッセージは破棄されます。有効期限は概算であり、セッションは設定した期間より最大30分長く (ただし短くはならず) 永続化される場合があります。

 

一時的な認証情報

AWS IoT Coreはアイデンティティプールを通じて取得した一時的な認証情報で動作できます。アイデンティティには2種類あります。

 

 

未認証

未認証の認証情報を使用している場合は、IAM メニューで自動的に作成された未認証ロールにポリシーをアタッチするだけです。次に、Cognito サービスが返した Access キー、Secret Key、Token を設定してクライアントを設定します。

未認証の認証情報を取得するための .NET コードを以下に示します

 


CognitoAWSCredentials credentials = new CognitoAWSCredentials(
    "us-east-1:cc3c9c48-646d-44ef-bfd5-0c5fb2f0882f", // Identity pool ID
    Amazon.RegionEndpoint.USEast1 // Region
);
 
var identityPoolId = credentials.GetCredentialsAsync();
 
AmazonCognitoIdentityClient cognitoClient = new AmazonCognitoIdentityClient(
    credentials, // the anonymous credentials
    Amazon.RegionEndpoint.USEast1 // the Amazon Cognito region
);
 
GetIdRequest idRequest = new GetIdRequest();
idRequest.AccountId = "222178873557";
idRequest.IdentityPoolId = "us-east-1:cc3c9c48-646d-44ef-bfd5-0c5fb2f0882f";
 
GetIdResponse idResp = cognitoClient.GetId(idRequest);
 
string AccessKey = identityPoolId.Result.AccessKey;
string SecretKey = identityPoolId.Result.SecretKey;
string SessionToken = identityPoolId.Result.Token;
 
string IdentityId = idResp.IdentityId;

認証済み

認証済み資格情報。IAM メニューで自動作成される認証済みロールにポリシーをアタッチし、AWS IoT Core ポリシーのユーザーポリシーをアタッチする必要があります。

IoT Core ポリシーメニューで新しいポリシーを作成し、新しいユーザーが認証するたびにこのポリシーをそのユーザーにアタッチします。

AWS の以下のコマンドを使用して、ポリシーをアタッチしたり Lambda 関数を作成したりできます。

 

aws iot attach-policy --policy-name PolicyName --target us-east-1:XXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

 

 

デバイスプロビジョニング

Fleet Provisioning サービスは以下の MQTT API 操作をサポートしています。

 

 

CreateCertificateFromCsr

 

CertificateSigningRequest をパラメータとして渡して CreateCertificateFromCsr メソッドを使用し、証明書を作成します。このリクエストへのレスポンスを受信するには、まず以下のメソッドにサブスクライブします: SubscribeCreateCertificateFromCsrResponse と SubscribeCreateCertificateFromCsrError。

 

CreateKeysAndCertificate

 

CreateKeysAndCertificate メソッドを使用して新しい証明書とキーを作成します。このリクエストへのレスポンスを受信するには、最初に以下のメソッドにサブスクライブします: SubscribeCreateKeysAndCertificateResponse および SubscribeCreateKeysAndCertificateError。

 

RegisterThing

 

RegisterThingメソッドを使用して、Template NameとJSON形式のPayloadをパラメータとして渡し、新しいthingを登録します。このリクエストへの応答を受信するには、まず次のメソッドSubscribeRegisterThingResponseとSubscribeRegisterThingErrorをサブスクライブします。