c语言 sdk,适用于 C 语言的 Azure IoT 设备 SDK
您現(xiàn)在訪問的是微軟AZURE全球版技術(shù)文檔網(wǎng)站,若需要訪問由世紀(jì)互聯(lián)運(yùn)營的MICROSOFT AZURE中國區(qū)技術(shù)文檔網(wǎng)站,請?jiān)L問 https://docs.azure.cn.
適用于 C 的 Azure IoT 設(shè)備 SDK
05/17/2019
本文內(nèi)容
Azure IoT 設(shè)備 SDK 是一個(gè)庫集,旨在簡化從 Azure IoT 中心 服務(wù)發(fā)送和接收消息的過程。 有各種不同的 SDK,每個(gè) SDK 都以特定的平臺為目標(biāo),而本文說明的是 適用于 C 語言的 Azure IoT 設(shè)備 SDK。
備注
嵌入式 C SDK 是支持自帶網(wǎng)絡(luò) (BYON) 方法的受限制設(shè)備的替代項(xiàng)。 IoT 開發(fā)人員可以自由選擇使用 MQTT 客戶端、TLS 和套接字來創(chuàng)建設(shè)備解決方案。 詳細(xì)了解嵌入式 C SDK。
備注
本文中提到的某些功能(例如云到設(shè)備消息傳遞、設(shè)備孿生、設(shè)備管理)僅在 IoT 中心的標(biāo)準(zhǔn)層中提供。 有關(guān)基本和標(biāo)準(zhǔn) IoT 中心層的詳細(xì)信息,請參閱如何選擇合適的 IoT 中心層。
適用于 C 語言的 Azure IoT 設(shè)備 SDK 以 ANSI C (C99) 編寫,以獲得最大可移植性。 此功能使得這些庫很適合在多個(gè)平臺和設(shè)備上運(yùn)行,尤其是在以將磁盤和內(nèi)存占用量降到最低作為優(yōu)先考慮的情況下。
SDK 已在許多平臺上進(jìn)行了測試(有關(guān)詳細(xì)信息,請參閱 Azure IoT 認(rèn)證設(shè)備目錄)。 盡管本文包含的是在 Windows 平臺上運(yùn)行的示例代碼演示,但本文所述的代碼在各種支持的平臺上都完全相同。
下面的視頻概述了適用于 C 語言的 Azure IoT SDK:
本文介紹適用于 C 語言的 Azure IoT 設(shè)備 SDK 的體系結(jié)構(gòu),將演示如何初始化設(shè)備庫,將數(shù)據(jù)發(fā)送到 IoT 中心,以及從 IoT 中心接收消息。 本文中的信息應(yīng)足以讓你開始使用 SDK,但同時(shí)也提供了有關(guān)庫的其他信息的鏈接。
SDK 體系結(jié)構(gòu)
可在 GitHub 存儲庫中找到 適用于 C 語言的 Azure IoT 設(shè)備 SDK,還可在 C API 參考中查看 API 的詳細(xì)信息。
在此存儲庫的主分支中可找到最新版本的庫:
此 SDK 的核心實(shí)現(xiàn)可在 iothub_client 文件夾中找到,此文件夾包含 SDK 的最低 API 層的實(shí)現(xiàn):IoTHubClient 庫。 此 IoTHubClient 庫包含實(shí)現(xiàn)原始消息傳送的 API,即將消息發(fā)送到 IoT 中心以及從 IoT 中心接收消息。 使用此庫時(shí),需要負(fù)責(zé)實(shí)現(xiàn)消息序列化,但與 IoT 中心通信的其他細(xì)節(jié)則由系統(tǒng)處理。
serializer 文件夾包含幫助器函數(shù)和示例代碼,演示了使用客戶端庫向 Azure IoT 中心發(fā)送消息之前如何序列化數(shù)據(jù)。 使用序列化程序不是必需的,僅為了提供便利。 如果使用 序列化程序 庫,需要定義一個(gè)模型,用于指定要發(fā)送到 IoT 中心的數(shù)據(jù)以及預(yù)期要從 IoT 中心接收的消息。 定義模型后,SDK 將提供一個(gè) API 圖面,讓你輕松處理設(shè)備到云和云到設(shè)備的消息,而無需擔(dān)心序列化細(xì)節(jié)。 此庫依賴于使用 MQTT 和 AMQP 等協(xié)議實(shí)現(xiàn)傳輸?shù)钠渌_放源代碼庫。
IoTHubClient 庫依賴于其他開放源代碼庫:
Azure C 共享實(shí)用程序庫,其常用功能用于很多 Azure 相關(guān)的 C SDK 中所需的基本任務(wù)(如字符串、列表操作和 IO 等)。
Azure uAMQP 庫,此庫是針對資源約束設(shè)備的 AMQP 客戶端實(shí)現(xiàn)的優(yōu)化。
Azure uMQTT 庫,它是實(shí)現(xiàn) MQTT 協(xié)議并針對資源約束設(shè)備進(jìn)行了優(yōu)化的通用型庫。
查看示例代碼可以更方便地了解這些庫的用法。 以下部分演練 SDK 中包含的幾個(gè)示例應(yīng)用程序。 此演練應(yīng)可讓你輕松了解 SDK 體系結(jié)構(gòu)層的各種功能以及 API 工作原理的簡介。
運(yùn)行示例之前
在面向 C 的 Azure IoT 設(shè)備 SDK 中運(yùn)行示例之前,必須在 Azure 訂閱中創(chuàng)建 IoT 中心服務(wù)的實(shí)例。 然后完成以下任務(wù):
準(zhǔn)備開發(fā)環(huán)境
獲取設(shè)備憑據(jù)。
準(zhǔn)備開發(fā)環(huán)境
為常用平臺提供了包(例如適用于 Windows 的 NuGet 包或者適用于 Debian 和 Ubuntu 的 apt_get),示例將使用這些包(如果適用)。 在某些情況下,需要為設(shè)備編譯 SDK,或者在設(shè)備上編譯 SDK。 如果需要編譯 SDK,請參閱 GitHub 存儲庫中的準(zhǔn)備開發(fā)環(huán)境。
若要獲取示例應(yīng)用程序代碼,請從 GitHub 下載 SDK 的副本。 從 GitHub 存儲庫的主分支獲取源的副本。
獲取設(shè)備憑據(jù)
獲取示例源代碼后,下一步是獲取一組設(shè)備憑據(jù)。 要使設(shè)備能夠訪問 IoT 中心,必須先將該設(shè)備添加到 IoT 中心標(biāo)識注冊表。 添加設(shè)備時(shí),需要獲取一組所需的設(shè)備憑據(jù),以便設(shè)備能夠連接到 IoT 中心。 下一部分所述示例應(yīng)用程序的預(yù)期憑據(jù)格式為 設(shè)備連接字符串。
有幾個(gè)開源工具可幫助你管理 IoT 中心。
一個(gè)是稱為 Azure IoT 資源管理器的 Windows 應(yīng)用程序。
一個(gè)稱為 Azure IoT 工具的跨平臺 Visual Studio Code 擴(kuò)展。
本教程使用圖形 設(shè)備資源管理器 工具。 如果在 VS Code 中進(jìn)行開發(fā),可以使用適用于 VS Code 的 Azure IoT 工具 。 如果喜歡使用 CLI 工具,也可以使用適用于 Azure CLI 2.0 的 IoT 擴(kuò)展 工具。
設(shè)備資源管理器工具使用 Azure IoT 服務(wù)庫在 IoT 中心執(zhí)行各種功能(包括添加設(shè)備)。 若使用設(shè)備資源管理器工具添加設(shè)備,會獲得設(shè)備的連接字符串。 需要此連接字符串才能運(yùn)行示例應(yīng)用程序。
如果不熟悉設(shè)備資源管理器工具,請參閱以下過程,了解如何使用該工具來添加設(shè)備和獲取設(shè)備連接字符串。
運(yùn)行該程序時(shí),可以看到以下界面:
在第一個(gè)字段中輸入用戶的 IoT 中心連接字符串,并單擊“更新”。 此步驟配置該工具,以便與 IoT 中心通信。
可以在“IoT 中心服務(wù)” > “設(shè)置” > “共享訪問策略” > “iothubowner”下找到 連接字符串。
配置 IoT 中心連接字符串后,請單擊“管理”選項(xiàng)卡:
可在此選項(xiàng)卡中管理已注冊到 IoT 中心的設(shè)備。
單擊“創(chuàng)建”按鈕創(chuàng)建設(shè)備。 會顯示一個(gè)已預(yù)先填充一組密鑰(主密鑰和輔助密鑰)的對話框。 輸入“設(shè)備 ID”,并單擊“創(chuàng)建”。
創(chuàng)建設(shè)備后,“設(shè)備”列表會更新,其中包含所有已注冊的設(shè)備(包括剛剛創(chuàng)建的設(shè)備)。 如果在新設(shè)備上單擊右鍵,會看到此菜單:
如果選擇“復(fù)制所選設(shè)備的連接字符串”,會將設(shè)備連接字符串復(fù)制到剪貼板。 請保留設(shè)備連接字符串的副本。 在運(yùn)行后續(xù)部分中所述的示例應(yīng)用程序時(shí),將要用到它。
完成上述步驟后,可以開始運(yùn)行一些代碼。 大多數(shù)示例的主源文件頂部都有一個(gè)常量,可讓你輸入連接字符串。 例如,iothub_client_samples_iothub_convenience_sample 應(yīng)用程序中的相應(yīng)行如下所示。
static const char* connectionString = "[device connection string]";
使用 IoTHubClient 庫
azure-iot-sdk-c 存儲庫的 iothub_client 文件夾中有一個(gè) samples 文件夾,其中包含名為 iothub_client_sample_mqtt 的應(yīng)用程序。
Windows 版本的 iothub_client_samples_iothub_convenience_sample 應(yīng)用程序包含以下 Visual Studio 解決方案:
備注
如果 Visual Studio 要求你將項(xiàng)目重新定位到最新版本,請接受提示。
此解決方案只包含一個(gè)項(xiàng)目。 此解決方案中安裝了四個(gè) NuGet 包:
Microsoft.Azure.C.SharedUtility
Microsoft.Azure.IoTHub.MqttTransport
Microsoft.Azure.IoTHub.IoTHubClient
Microsoft.Azure.umqtt
在使用 SDK 時(shí)始終需要 Microsoft.Azure.C.SharedUtility 包。 本示例使用 MQTT 協(xié)議,因此,必須包括 Microsoft.Azure.umqtt 和 Microsoft.Azure.IoTHub.MqttTransport 包(AMQP 和 HTTPS 有對應(yīng)的包)。 由于此示例使用 IoTHubClient 庫,因此還必須在解決方案中包含 Microsoft.Azure.IoTHub.IoTHubClient 包。
可以在 iothub_client_samples_iothub_convenience_sample 源文件中找到示例應(yīng)用程序的實(shí)現(xiàn)。
以下步驟使用此示例應(yīng)用程序來演示使用 IoTHubClient 庫時(shí)所需的項(xiàng)目。
初始化庫
備注
在開始使用庫之前,可能需要執(zhí)行一些特定于平臺的初始化。 例如,如果打算在 Linux 上使用 AMQP,則必須初始化 OpenSSL 庫。 GitHub 存儲庫中的示例在客戶端啟動(dòng)時(shí)調(diào)用實(shí)用工具函數(shù) platform_init,并在退出之前調(diào)用 platform_deinit 函數(shù)。 這些函數(shù)在 platform.h 標(biāo)頭文件中聲明。 應(yīng)該在存儲庫中為目標(biāo)平臺檢查這些函數(shù)定義,以確定是否需要在客戶端中包含任何特定于平臺的初始化代碼。
只有在分配 IoT 中心客戶端句柄之后,才可以開始使用庫:
if ((iotHubClientHandle =
IoTHubClient_LL_CreateFromConnectionString(connectionString, MQTT_Protocol)) == NULL)
{
(void)printf("ERROR: iotHubClientHandle is NULL!\r\n");
}
else
{
...
將設(shè)備資源管理器工具獲取的設(shè)備連接字符串傳遞給此函數(shù)。 還需指定要使用的通信協(xié)議。 本示例使用 MQTT,但也可以選擇 AMQP 和 HTTPS。
獲取有效的 IOTHUB_CLIENT_HANDLE 后,可以開始調(diào)用 API 來與 IoT 中心相互發(fā)送和接收消息。
發(fā)送消息
示例應(yīng)用程序?qū)⒃O(shè)置一個(gè)循環(huán)用于向 IoT 中心發(fā)送消息。 以下代碼片段:
創(chuàng)建消息。
將屬性添加到消息。
發(fā)送消息。
首先創(chuàng)建一條消息:
size_t iterator = 0;
do
{
if (iterator < MESSAGE_COUNT)
{
sprintf_s(msgText, sizeof(msgText), "{\"deviceId\":\"myFirstDevice\",\"windSpeed\":%.2f}", avgWindSpeed + (rand() % 4 + 2));
if ((messages[iterator].messageHandle = IoTHubMessage_CreateFromByteArray((const unsigned char*)msgText, strlen(msgText))) == NULL)
{
(void)printf("ERROR: iotHubMessageHandle is NULL!\r\n");
}
else
{
messages[iterator].messageTrackingId = iterator;
MAP_HANDLE propMap = IoTHubMessage_Properties(messages[iterator].messageHandle);
(void)sprintf_s(propText, sizeof(propText), "PropMsg_%zu", iterator);
if (Map_AddOrUpdate(propMap, "PropName", propText) != MAP_OK)
{
(void)printf("ERROR: Map_AddOrUpdate Failed!\r\n");
}
if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messages[iterator].messageHandle, SendConfirmationCallback, &messages[iterator]) != IOTHUB_CLIENT_OK)
{
(void)printf("ERROR: IoTHubClient_LL_SendEventAsync..........FAILED!\r\n");
}
else
{
(void)printf("IoTHubClient_LL_SendEventAsync accepted message [%d] for transmission to IoT Hub.\r\n", (int)iterator);
}
}
}
IoTHubClient_LL_DoWork(iotHubClientHandle);
ThreadAPI_Sleep(1);
iterator++;
} while (g_continueRunning);
每次發(fā)送消息時(shí),指定發(fā)送數(shù)據(jù)時(shí)所調(diào)用的回調(diào)函數(shù)的引用。 在此示例中,回調(diào)函數(shù)名為 SendConfirmationCallback。 以下代碼片段演示此回調(diào)函數(shù):
static void SendConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback)
{
EVENT_INSTANCE* eventInstance = (EVENT_INSTANCE*)userContextCallback;
(void)printf("Confirmation[%d] received for message tracking id = %zu with result = %s\r\n", callbackCounter, eventInstance->messageTrackingId, MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result));
/* Some device specific action code goes here... */
callbackCounter++;
IoTHubMessage_Destroy(eventInstance->messageHandle);
}
處理完消息后,請注意對 IoTHubMessage_Destroy 函數(shù)的調(diào)用。 此函數(shù)釋放創(chuàng)建消息時(shí)分配的資源。
接收消息
接收消息是一個(gè)異步操作。 首先,請注冊當(dāng)設(shè)備接收消息時(shí)所要調(diào)用的回調(diào):
if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, ReceiveMessageCallback, &receiveContext) != IOTHUB_CLIENT_OK)
{
(void)printf("ERROR: IoTHubClient_LL_SetMessageCallback..........FAILED!\r\n");
}
else
{
(void)printf("IoTHubClient_LL_SetMessageCallback...successful.\r\n");
...
最后一個(gè)參數(shù)是指向所需對象的 void 指針。 在本示例中,這是一個(gè)指向整數(shù)的指針,但也可以是指向更復(fù)雜數(shù)據(jù)結(jié)構(gòu)的指針。 此參數(shù)使回調(diào)函數(shù)可與此函數(shù)的調(diào)用方以共享狀態(tài)運(yùn)行。
當(dāng)設(shè)備接收消息時(shí),將調(diào)用注冊的回調(diào)函數(shù)。 此回調(diào)函數(shù):
從消息中檢索消息 ID 和相關(guān) ID。
檢索消息內(nèi)容。
從消息中檢索任何自定義屬性。
static IOTHUBMESSAGE_DISPOSITION_RESULT ReceiveMessageCallback(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback)
{
int* counter = (int*)userContextCallback;
const char* buffer;
size_t size;
MAP_HANDLE mapProperties;
const char* messageId;
const char* correlationId;
// Message properties
if ((messageId = IoTHubMessage_GetMessageId(message)) == NULL)
{
messageId = "";
}
if ((correlationId = IoTHubMessage_GetCorrelationId(message)) == NULL)
{
correlationId = "";
}
// Message content
if (IoTHubMessage_GetByteArray(message, (const unsigned char**)&buffer, &size) != IOTHUB_MESSAGE_OK)
{
(void)printf("unable to retrieve the message data\r\n");
}
else
{
(void)printf("Received Message [%d]\r\n Message ID: %s\r\n Correlation ID: %s\r\n Data: <<>> & Size=%d\r\n", *counter, messageId, correlationId, (int)size, buffer, (int)size);
// If we receive the work 'quit' then we stop running
if (size == (strlen("quit") * sizeof(char)) && memcmp(buffer, "quit", size) == 0)
{
g_continueRunning = false;
}
}
// Retrieve properties from the message
mapProperties = IoTHubMessage_Properties(message);
if (mapProperties != NULL)
{
const char*const* keys;
const char*const* values;
size_t propertyCount = 0;
if (Map_GetInternals(mapProperties, &keys, &values, &propertyCount) == MAP_OK)
{
if (propertyCount > 0)
{
size_t index;
printf(" Message Properties:\r\n");
for (index = 0; index < propertyCount; index++)
{
(void)printf("\tKey: %s Value: %s\r\n", keys[index], values[index]);
}
(void)printf("\r\n");
}
}
}
/* Some device specific action code goes here... */
(*counter)++;
return IOTHUBMESSAGE_ACCEPTED;
}
使用 IoTHubMessage_GetByteArray 函數(shù)來檢索消息(在本示例中是一個(gè)字符串)。
取消初始化庫
完成發(fā)送事件和接收消息后,可以取消初始化 IoT 庫。 為此,請發(fā)出以下函數(shù)調(diào)用:
IoTHubClient_LL_Destroy(iotHubClientHandle);
此調(diào)用釋放 IoTHubClient_CreateFromConnectionString 函數(shù)以前分配的資源。
可以看到,使用 IoTHubClient 庫可以輕松發(fā)送和接收消息。 該庫處理與 IoT 中心通信的詳細(xì)信息,包括要使用哪個(gè)協(xié)議(從開發(fā)人員的視角來看,這是一個(gè)簡單的配置選項(xiàng))。
在如何對設(shè)備發(fā)送到 IoT 中心的數(shù)據(jù)進(jìn)行序列化方面,IoTHubClient 庫也可提供精確的控制。 在某些情況下,這種控制級別是一項(xiàng)優(yōu)點(diǎn),但在其他情況下,這可能不是你想要看到的實(shí)現(xiàn)細(xì)節(jié)。 如果是這樣,可以考慮使用下一部分中介紹的 序列化程序 庫。
使用序列化程序庫
從概念上講,序列化程序 庫位于 SDK 中的 IoTHubClient 庫之上。 它使用 IoTHubClient 庫來與 IoT 中心進(jìn)行底層通信,但它添加了建模功能,消除了開發(fā)人員處理消息序列化的負(fù)擔(dān)。 我們通過一個(gè)示例充分演示此庫的工作原理。
azure-iot-sdk-c 存儲庫的 serializer 文件夾中有一個(gè) samples 文件夾,其中包含名為 simplesample_mqtt 的應(yīng)用程序。 此示例的 Windows 版本包含以下 Visual Studio 解決方案:
備注
如果 Visual Studio 要求你將項(xiàng)目重新定位到最新版本,請接受提示。
如同前面的示例,此示例也包含多個(gè) NuGet 包:
Microsoft.Azure.C.SharedUtility
Microsoft.Azure.IoTHub.MqttTransport
Microsoft.Azure.IoTHub.IoTHubClient
Microsoft.Azure.IoTHub.Serializer
Microsoft.Azure.umqtt
其中的大多數(shù)包已在前面的示例中出現(xiàn)過,但 Microsoft.Azure.IoTHub.Serializer 是新的。 使用 序列化程序 庫時(shí)需要此包。
可以在 iothub_client_samples_iothub_convenience_sample 文件中找到示例應(yīng)用程序的實(shí)現(xiàn)。
以下部分演練本示例的重要組成部分。
初始化庫
若要開始使用 序列化程序 庫,請調(diào)用初始化 API:
if (serializer_init(NULL) != SERIALIZER_OK)
{
(void)printf("Failed on serializer_init\r\n");
}
else
{
IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, MQTT_Protocol);
srand((unsigned int)time(NULL));
int avgWindSpeed = 10;
if (iotHubClientHandle == NULL)
{
(void)printf("Failed on IoTHubClient_LL_Create\r\n");
}
else
{
ContosoAnemometer* myWeather = CREATE_MODEL_INSTANCE(WeatherStation, ContosoAnemometer);
if (myWeather == NULL)
{
(void)printf("Failed on CREATE_MODEL_INSTANCE\r\n");
}
else
{
...
對 serializer_init 函數(shù)進(jìn)行的調(diào)用是一次性調(diào)用,用于初始化底層庫。 然后,需調(diào)用 IoTHubClient_LL_CreateFromConnectionString 函數(shù),這是 IoTHubClient 示例中的同一 API。 此調(diào)用將設(shè)置設(shè)備連接字符串(也可用于選擇要使用的協(xié)議)。 本示例使用 MQTT 作為傳輸方式,但也可以使用 AMQP 或 HTTPS。
最后,調(diào)用 CREATE_MODEL_INSTANCE 函數(shù)。 WeatherStation 是模型的命名空間,ContosoAnemometer 是模型的名稱。 創(chuàng)建模型實(shí)例后,可以使用它來開始發(fā)送和接收消息。 但是,必須了解模型是什么。
定義模型
序列化程序 庫中的模型定義了設(shè)備可發(fā)送到 IoT 中心的消息以及可接收的消息(在建模語言中稱為 操作)。 如 iothub_client_samples_iothub_convenience_sample 示例應(yīng)用程序中所示,你使用一組 C 宏定義了一個(gè)模塊:
BEGIN_NAMESPACE(WeatherStation);
DECLARE_MODEL(ContosoAnemometer,
WITH_DATA(ascii_char_ptr, DeviceId),
WITH_DATA(int, WindSpeed),
WITH_ACTION(TurnFanOn),
WITH_ACTION(TurnFanOff),
WITH_ACTION(SetAirResistance, int, Position)
);
END_NAMESPACE(WeatherStation);
BEGIN_NAMESPACE 和 END_NAMESPACE 這兩個(gè)宏都以模型的命名空間作為參數(shù)。 介于這兩個(gè)宏之間的內(nèi)容應(yīng)該就是模型的定義和模型使用的數(shù)據(jù)結(jié)構(gòu)。
在本示例中,有一個(gè)名為 ContosoAnemometer 的模型。 此模型定義了設(shè)備可以發(fā)送到 IoT 中心的兩個(gè)數(shù)據(jù)片段:DeviceId 和 WindSpeed。 它還定義了設(shè)備可以接收的三個(gè)操作(消息):TurnFanOn、TurnFanOff 和 SetAirResistance。 每個(gè)數(shù)據(jù)元素都有一個(gè)類型,而每個(gè)操作都有一個(gè)名稱(以及一組可選參數(shù))。
模型中定義的數(shù)據(jù)和操作可定義 API 接口,此接口可用于將消息發(fā)送到 IoT 中心,以及響應(yīng)發(fā)送到設(shè)備的消息。 最好通過示例了解此模型的用法。
發(fā)送消息
模型定義了可以發(fā)送到 IoT 中心的數(shù)據(jù)。 在本示例中,這是指使用 WITH_DATA 宏來定義的兩個(gè)數(shù)據(jù)項(xiàng)之一。 要將 DeviceId 和 WindSpeed 值發(fā)送到 IoT 中心,需要執(zhí)行幾個(gè)步驟。 第一個(gè)步驟是設(shè)置要發(fā)送的數(shù)據(jù):
myWeather->DeviceId = "myFirstDevice";
myWeather->WindSpeed = avgWindSpeed + (rand() % 4 + 2);
使用前面定義的模型可以通過設(shè)置 struct 的成員來設(shè)置值。 接下來,序列化想要發(fā)送的消息:
unsigned char* destination;
size_t destinationSize;
if (SERIALIZE(&destination, &destinationSize, myWeather->DeviceId, myWeather->WindSpeed) != CODEFIRST_OK)
{
(void)printf("Failed to serialize\r\n");
}
else
{
sendMessage(iotHubClientHandle, destination, destinationSize);
free(destination);
}
此代碼將設(shè)備到云的消息序列化到緩沖區(qū)(由 destination 引用)。 然后,代碼調(diào)用 sendMessage 函數(shù)將消息發(fā)送到 IoT 中心:
static void sendMessage(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const unsigned char* buffer, size_t size)
{
static unsigned int messageTrackingId;
IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(buffer, size);
if (messageHandle == NULL)
{
printf("unable to create a new IoTHubMessage\r\n");
}
else
{
if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)(uintptr_t)messageTrackingId) != IOTHUB_CLIENT_OK)
{
printf("failed to hand over the message to IoTHubClient");
}
else
{
printf("IoTHubClient accepted the message for delivery\r\n");
}
IoTHubMessage_Destroy(messageHandle);
}
messageTrackingId++;
}
IoTHubClient_LL_SendEventAsync 的倒數(shù)第二個(gè)參數(shù)是對成功發(fā)送數(shù)據(jù)后所調(diào)用的回調(diào)函數(shù)的引用。 下面是本示例中的回調(diào)函數(shù):
void sendCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback)
{
unsigned int messageTrackingId = (unsigned int)(uintptr_t)userContextCallback;
(void)printf("Message Id: %u Received.\r\n", messageTrackingId);
(void)printf("Result Call Back Called! Result is: %s \r\n", MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result));
}
第二個(gè)參數(shù)是指向用戶上下文的指針,即傳遞給 IoTHubClient_LL_SendEventAsync 的同一個(gè)指針。 在本例中,該上下文是一個(gè)簡易計(jì)數(shù)器,但也可以是所需的任何組件。
這就是發(fā)送設(shè)備到云的消息所要執(zhí)行的所有操作。 最后要介紹的內(nèi)容是如何接收消息。
接收消息
接收消息的方式類似于在 IoTHubClient 庫中處理消息。 首先,需要注冊消息回調(diào)函數(shù):
if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle,
IoTHubMessage, myWeather) != IOTHUB_CLIENT_OK)
{
printf("unable to IoTHubClient_SetMessageCallback\r\n");
}
else
{
...
然后編寫在接收消息時(shí)要調(diào)用的回調(diào)函數(shù):
static IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubMessage(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback)
{
IOTHUBMESSAGE_DISPOSITION_RESULT result;
const unsigned char* buffer;
size_t size;
if (IoTHubMessage_GetByteArray(message, &buffer, &size) != IOTHUB_MESSAGE_OK)
{
printf("unable to IoTHubMessage_GetByteArray\r\n");
result = IOTHUBMESSAGE_ABANDONED;
}
else
{
/*buffer is not zero terminated*/
char* temp = malloc(size + 1);
if (temp == NULL)
{
printf("failed to malloc\r\n");
result = IOTHUBMESSAGE_ABANDONED;
}
else
{
(void)memcpy(temp, buffer, size);
temp[size] = '\0';
EXECUTE_COMMAND_RESULT executeCommandResult = EXECUTE_COMMAND(userContextCallback, temp);
result =
(executeCommandResult == EXECUTE_COMMAND_ERROR) ? IOTHUBMESSAGE_ABANDONED :
(executeCommandResult == EXECUTE_COMMAND_SUCCESS) ? IOTHUBMESSAGE_ACCEPTED :
IOTHUBMESSAGE_REJECTED;
free(temp);
}
}
return result;
}
此代碼是一個(gè)樣板 - 對任何解決方案都是相同的。 此函數(shù)將接收消息并通過調(diào)用 EXECUTE_COMMAND 將它路由到相應(yīng)的函數(shù)。 此時(shí)調(diào)用的函數(shù)取決于模型中的操作定義。
在模型中定義操作時(shí),需要實(shí)現(xiàn)當(dāng)設(shè)備接收相應(yīng)的消息時(shí)調(diào)用的函數(shù)。 例如,如果模型定義了此操作:
WITH_ACTION(SetAirResistance, int, Position)
使用此簽名定義函數(shù):
EXECUTE_COMMAND_RESULT SetAirResistance(ContosoAnemometer* device, int Position)
{
(void)device;
(void)printf("Setting Air Resistance Position to %d.\r\n", Position);
return EXECUTE_COMMAND_SUCCESS;
}
請注意,函數(shù)的名稱與模型中的操作名稱匹配,而函數(shù)的參數(shù)與為該操作指定的參數(shù)匹配。 第一個(gè)參數(shù)始終是必需的,包含指向模型實(shí)例的指針。
當(dāng)設(shè)備收到與此簽名匹配的消息時(shí),會調(diào)用相應(yīng)的函數(shù)。 因此,除了必須包含 IoTHubMessage 中的樣板代碼以外,接收消息所涉及的操作只是為模型中定義的每個(gè)操作定義一個(gè)簡單的函數(shù)。
取消初始化庫
完成發(fā)送數(shù)據(jù)和接收消息后,可以取消初始化 IoT 庫。
...
DESTROY_MODEL_INSTANCE(myWeather);
}
IoTHubClient_LL_Destroy(iotHubClientHandle);
}
serializer_deinit();
上述 3 個(gè)函數(shù)均符合以前所述的 3 個(gè)初始化函數(shù)。 調(diào)用這些 API 可確保釋放以前分配的資源。
后續(xù)步驟
本文介紹了有關(guān)使用 適用于 C 語言的 Azure IoT 設(shè)備 SDK 中的庫的基本知識。其中針對 SDK 中包含的組件及其體系結(jié)構(gòu),以及如何開始使用 Windows 示例等進(jìn)行了詳細(xì)說明。 下一篇文章通過講解有關(guān) IoTHubClient 庫的詳細(xì)信息來繼續(xù)介紹該 SDK。
若要詳細(xì)了解如何針對 IoT 中心進(jìn)行開發(fā),請參閱 Azure IoT SDK。
若要進(jìn)一步探索 IoT 中心的功能,請參閱:
總結(jié)
以上是生活随笔為你收集整理的c语言 sdk,适用于 C 语言的 Azure IoT 设备 SDK的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux whois rpm,Cent
- 下一篇: C语言指令启动mcs51计时器是,嵌入式