Azure IoT Hubにメッセージ送信する最小コード

Azure Sphere SDKには、Azure IoT Hubに接続するアプリケーション、Azure IoT Hub Sample for MT3620 RDBのプロジェクトテンプレートが含まれており、Azure SphereからAzure IoT Hubに接続するアプリケーションを簡単に作ることができます。

ただ、生成されたコードはデバイスツインやダイレクトメソッドなどテンコ盛りで、コールバックを使っていることから、分かりやすいコードとは言えません。
そこで、きちんとした作りではありませんが、理解しやすいコードを紹介します。

# プロジェクトを作成

`Blank Application for MT3620 RDB (Azure Sphere)`のプロジェクトを新規作成します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/109634/089523bf-0a43-f437-d2f3-1cf5bc26334e.png)

# ライブラリを追加

`libazureiot.so`の読み込みを追加します。
具体的には、プロジェクトのプロパティにある、Linker - InputのAdditional Dependenciesに`-lazureiot`を追加してください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/109634/1a04d941-9143-af45-557f-c037b88045e3.png)

# ヘッダファイルをインクルード

IoTHubDeviceClient_LL_Xxxx関数の呼び出しに必要なヘッダファイル、`iothub_device_client_ll.h`と`iothubtransportmqtt.h`をインクルードします。

```c
#include <azureiot/iothubtransportmqtt.h>
#include <azureiot/iothub_device_client_ll.h>
```

あと、簡易なエラーチェックとしてassert()を使うので、`assert.h`もインクルードしてください。

```c
#include <assert.h>
```

# 接続文字列

接続文字列を`CONNECTION_STRING`という名前で定義します。

```c
#define CONNECTION_STRING	"具体的な接続文字列"
```

# 接続の許可

接続先のホスト名を、app_manifest.jsonの`AllowedConnections`に追加します。

```json
...
    "AllowedConnections": [ "名前.azure-devices.net" ],
...
```

# 証明書

ルートCA証明書を`azureIoTCertificatesX`という名前で定義します。

```c
static const char azureIoTCertificatesX[] =
/* Baltimore */
"-----BEGIN CERTIFICATE-----\r\n"
"MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ\r\n"
"RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\r\n"
"VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX\r\n"
"DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y\r\n"
"ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy\r\n"
"VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr\r\n"
"mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr\r\n"
"IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK\r\n"
"mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\r\n"
"XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy\r\n"
"dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye\r\n"
"jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1\r\n"
"BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\r\n"
"DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92\r\n"
"9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx\r\n"
"jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0\r\n"
"Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz\r\n"
"ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\r\n"
"R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\r\n"
"-----END CERTIFICATE-----\r\n";
```

# メッセージ送信するコード（断片）

## クライアントハンドルの作成

`IoTHubDeviceClient_LL_CreateFromConnectionString()`で、接続文字列からクライアントハンドルを作成します。

```c
	IOTHUB_DEVICE_CLIENT_LL_HANDLE clientHandle = IoTHubDeviceClient_LL_CreateFromConnectionString(CONNECTION_STRING, MQTT_Protocol);
	assert(clientHandle != NULL);
```

## 証明書を設定

`IoTHubDeviceClient_LL_SetOption()`で、クライアントハンドルに証明書を設定します。

```c
	IOTHUB_CLIENT_RESULT result;
	result = IoTHubDeviceClient_LL_SetOption(clientHandle, "TrustedCerts", azureIoTCertificatesX);
	assert(result == IOTHUB_CLIENT_OK);
```

## メッセージを送信

`IoTHubDeviceClient_LL_SendEventAsync()`で、クライアントハンドルに送信するメッセージを追加します。
> 実際には、このタイミングでは送信しません。
> 後の`IoTHubDeviceClient_LL_DoWork()`でAzure IoT Hubに接続して送信します。

```c
	IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromString("Hello world.");
	assert(messageHandle != NULL);
	result = IoTHubDeviceClient_LL_SendEventAsync(clientHandle, messageHandle, NULL, 0);
	assert(result == IOTHUB_CLIENT_OK);
	IoTHubMessage_Destroy(messageHandle);
```

## 実行

`IoTHubDeviceClient_LL_DoWork()`で、クライアントに関する処理を実行します。

```c
    while (!terminationRequired)
	{
		IoTHubDeviceClient_LL_DoWork(clientHandle);
	}
```

## クライアントハンドルの破棄

`IoTHubDeviceClient_LL_Destroy()`で、クライアントハンドルを破棄（終了）します。

```c
	IoTHubDeviceClient_LL_Destroy(clientHandle);
```

# メッセージ送信するコード（全部）

```c
int main(int argc, char *argv[])
{
    Log_Debug("Application starting.\n");

    // Register a SIGTERM handler for termination requests
    struct sigaction action;
    memset(&action, 0, sizeof(struct sigaction));
    action.sa_handler = TerminationHandler;
    sigaction(SIGTERM, &action, NULL);

	IOTHUB_DEVICE_CLIENT_LL_HANDLE clientHandle = IoTHubDeviceClient_LL_CreateFromConnectionString(CONNECTION_STRING, MQTT_Protocol);
	assert(clientHandle != NULL);

	IOTHUB_CLIENT_RESULT result;
	result = IoTHubDeviceClient_LL_SetOption(clientHandle, "TrustedCerts", azureIoTCertificatesX);
	assert(result == IOTHUB_CLIENT_OK);

	IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromString("Hello world.");
	assert(messageHandle != NULL);
	result = IoTHubDeviceClient_LL_SendEventAsync(clientHandle, messageHandle, NULL, 0);
	assert(result == IOTHUB_CLIENT_OK);
	IoTHubMessage_Destroy(messageHandle);

    while (!terminationRequired)
	{
		IoTHubDeviceClient_LL_DoWork(clientHandle);
	}

	IoTHubDeviceClient_LL_Destroy(clientHandle);

    Log_Debug("Application exiting.\n");
    return 0;
}
```

# 補足

* Azure Sphere SDKに含まれているAzure IoT Hub関連APIは[Azure IoT Device C SDK](https://github.com/Azure/azure-iot-sdk-c)のサブセット。
* `iothub_device_client_ll.h`を使う。同等APIの`iothub_client_ll.h`は使えない。（というか、無い。）