Azure IoT SDK per dispositivi per CAzure IoT device SDK for C

Azure IoT SDK per dispositivi è un set di librerie concepite per semplificare l'invio di messaggi e la ricezione di messaggi nel servizio Hub IoT di Azure.The Azure IoT device SDK is a set of libraries designed to simplify the process of sending messages to and receiving messages from the Azure IoT Hub service. Esistono diverse varianti dell'SDK, ognuna destinata a una piattaforma specifica, ma questo articolo illustrerà Azure IoT device SDK per C.There are different variations of the SDK, each targeting a specific platform, but this article describes the Azure IoT device SDK for C.

Azure IoT device SDK per C è scritto in ANSI C (C99) per ottimizzare la portabilità.The Azure IoT device SDK for C is written in ANSI C (C99) to maximize portability. Questa funzionalità rende le librerie particolarmente adatte al funzionamento su più dispositivi e piattaforme, specialmente quando la riduzione del footprint del disco e della memoria costituisce una priorità.This feature makes the libraries well-suited to operate on multiple platforms and devices, especially where minimizing disk and memory footprint is a priority.

Esiste una vasta gamma di piattaforme in cui è stato testato l'SDK. Per altri dettagli, vedere l'elenco dei dispositivi Azure Certified per IoT.There are a broad range of platforms on which the SDK has been tested (see the Azure Certified for IoT device catalog for details). Anche se questo articolo include procedure dettagliate di codice di esempio eseguito su piattaforma Windows, il codice descritto è identico per tutta la gamma di piattaforme supportate.Although this article includes walkthroughs of sample code running on the Windows platform, the code described in this article is identical across the range of supported platforms.

Questo articolo introduce l'architettura di Azure IoT SDK per dispositivi per C. Illustra come inizializzare la libreria di dispositivi, inviare dati all'hub IoT e riceverne i messaggi.This article introduces you to the architecture of the Azure IoT device SDK for C. It demonstrates how to initialize the device library, send data to IoT Hub, and receive messages from it. Le informazioni contenute in questo articolo dovrebbero essere sufficienti per iniziare a usare l'SDK, ma forniscono anche puntatori per ottenere informazioni aggiuntive sulle librerie.The information in this article should be enough to get started using the SDK, but also provides pointers to additional information about the libraries.

Architettura dell'SDKSDK architecture

È possibile trovare il repository GitHub di Azure IoT SDK per dispositivi per C e visualizzare i dettagli dell'API nelle informazioni di riferimento per l'API C.You can find the Azure IoT device SDK for C GitHub repository and view details of the API in the C API reference.

L'ultima versione delle librerie è disponibile nel ramo master del repository:The latest version of the libraries can be found in the master branch of the repository:

  • L'implementazione di base dell'SDK è nella cartella iothub_client contenente l'implementazione del livello di API più basso nell'SDK: la libreria IoTHubClient.The core implementation of the SDK is in the iothub_client folder that contains the implementation of the lowest API layer in the SDK: the IoTHubClient library. La libreria IoTHubClient contiene le API di implementazione di messaggistica non elaborata per inviare all'hub IoT i messaggi e per ricevere i messaggi dall'hub IoT.The IoTHubClient library contains APIs implementing raw messaging for sending messages to IoT Hub and receiving messages from IoT Hub. Quando si usa questa libreria, si dovrà implementare la serializzazione dei messaggi, mente gli altri dettagli della comunicazione con l'hub IoT vengono gestiti automaticamente.When using this library, you are responsible for implementing message serialization, but other details of communicating with IoT Hub are handled for you.
  • La cartella serializer contiene le funzioni di supporto e gli esempi che mostrano come serializzare i dati prima dell'invio all'hub IoT di Azure tramite la libreria client.The serializer folder contains helper functions and samples that show you how to serialize data before sending to Azure IoT Hub using the client library. L'uso del serializzatore, fornito per comodità, non è obbligatorio.The use of the serializer is not mandatory and is provided as a convenience. Per usare la libreria serializer, si definisce un modello che specifica i dati da inviare all'hub IoT e i messaggi che si prevede di ricevere dall'hub stesso.To use the serializer library, you define a model that specifies the data to send to IoT Hub and the messages you expect to receive from it. Dopo la definizione del modello, l'SDK fornisce tuttavia una superficie dell'API che consente di usare facilmente messaggi da dispositivo a cloud e da cloud a dispositivo senza doversi occupare dei dettagli relativi alla serializzazione.Once the model is defined, the SDK provides you with an API surface that enables you to easily work with device-to-cloud and cloud-to-device messages without worrying about the serialization details. La libreria dipende dall'implementazione del trasporto tramite protocolli come MQTT e AMQP da parte di altre librerie open source.The library depends on other open source libraries that implement transport using protocols such as MQTT and AMQP.
  • La libreria IoTHubClient dipende da altre librerie open source:The IoTHubClient library depends on other open source libraries:
    • La libreria Azure C shared utility, che offre funzionalità comuni per le attività di base, ad esempio stringhe, manipolazione elenco e I/O, necessarie in diversi SDK per C relativi ad Azure.The Azure C shared utility library, which provides common functionality for basic tasks (such as strings, list manipulation, and IO) needed across several Azure-related C SDKs.
    • La libreria Azure uAMQP, che è un'implementazione lato client di AMQP ottimizzata per i dispositivi con vincoli di risorse.The Azure uAMQP library, which is a client-side implementation of AMQP optimized for resource constrained devices.
    • La libreria Azure uMQTT, che è una libreria generica per l'implementazione del protocollo MQTT, ottimizzata per i dispositivi con vincoli di risorse.The Azure uMQTT library, which is a general-purpose library implementing the MQTT protocol and optimized for resource constrained devices.

L'uso di queste librerie è più facilmente comprensibile osservando esempi di codice.Use of these libraries is easier to understand by looking at example code. Le sezioni seguenti illustrano in dettaglio alcune applicazioni di esempio incluse nell'SDK.The following sections walk you through several of the sample applications that are included in the SDK. Questa procedura guidata offre una panoramica delle diverse funzionalità dei livelli architetturali dell'SDK e un'introduzione al funzionamento delle API.This walkthrough should give you a good feel for the various capabilities of the architectural layers of the SDK and an introduction to how the APIs work.

Prima di eseguire gli esempiBefore you run the samples

Prima di poter eseguire gli esempi in Azure IoT SDK per dispositivi per C, è necessario creare un'istanza del servizio hub IoT nella sottoscrizione di Azure.Before you can run the samples in the Azure IoT device SDK for C, you must create an instance of the IoT Hub service in your Azure subscription. Completare quindi le attività seguenti:Then complete the following tasks:

  • Preparare l'ambiente di sviluppoPrepare your development environment
  • Ottenere le credenziali del dispositivo.Obtain device credentials.

Preparare l'ambiente di sviluppoPrepare your development environment

Sono disponibili pacchetti per le piattaforme comuni (ad esempio, NuGet per Windows o apt_get per Debian e Ubuntu), che vengono usati negli esempi.Packages are provided for common platforms (such as NuGet for Windows or apt_get for Debian and Ubuntu) and the samples use these packages when available. In alcuni casi, è necessario compilare l'SDK per o nel dispositivo.In some cases, you need to compile the SDK for or on your device. Se è necessario compilare l'SDK, vedere Prepare your development environment (Preparare l'ambiente di sviluppo) nel repository GitHub.If you need to compile the SDK, see Prepare your development environment in the GitHub repository.

Per ottenere il codice di applicazione di esempio, scaricare una copia dell'SDK da GitHub.To obtain the sample application code, download a copy of the SDK from GitHub. Ottenere la copia del codice sorgente dal ramo master del repository GitHub.Get your copy of the source from the master branch of the GitHub repository.

Ottenere le credenziali del dispositivoObtain the device credentials

Quando il codice sorgente di esempio è disponibile, è necessario ottenere un set di credenziali del dispositivo.Now that you have the sample source code, the next thing to do is to get a set of device credentials. Perché un dispositivo possa accedere all'hub IoT, è necessario aggiungere prima di tutto il dispositivo al registro delle identità dell'hub IoT.For a device to be able to access an IoT hub, you must first add the device to the IoT Hub identity registry. Quando si aggiunge il dispositivo, si ottiene un set di credenziali del dispositivo, che sono necessarie per consentire la connessione del dispositivo all'hub IoT.When you add your device, you get a set of device credentials that you need for the device to be able to connect to the IoT hub. Le applicazioni di esempio illustrate nella sezione successiva richiedono queste credenziali sotto forma di stringa di connessione del dispositivo.The sample applications discussed in the next section expect these credentials in the form of a device connection string.

Esistono diversi strumenti open source che consentono di gestire l'hub IoT.There are several open source tools to help you manage your IoT hub.

Questa esercitazione usa lo strumento grafico Device Explorer.This tutorial uses the graphical device explorer tool. È anche possibile usare iothub-explorer se si preferisce uno strumento dell'interfaccia della riga di comando.You can also use the iothub-explorer tool if you prefer to use a CLI tool.

Lo strumento Device Explorer usa le librerie del servizio IoT di Azure per eseguire diverse funzioni nell'hub IoT, inclusa l'aggiunta di dispositivi.The device explorer tool uses the Azure IoT service libraries to perform various functions on IoT Hub, including adding devices. Se si usa lo strumento Device Explorer per aggiungere un dispositivo, si ottiene una stringa di connessione per il dispositivo,If you use the device explorer tool to add a device, you get a connection string for your device. che sarà necessaria per eseguire le applicazioni di esempio.You need this connection string to run the sample applications.

Se non si ha familiarità con lo strumento Device Explorer, la procedura seguente descrive come usarlo per aggiungere un dispositivo e ottenere una stringa di connessione del dispositivo.If you're not familiar with the device explorer tool, the following procedure describes how to use it to add a device and obtain a device connection string.

Per installare lo strumento Device Explorer, vedere How to use the Device Explorer for IoT Hub devices (Come usare Device Explorer per i dispositivi dell'hub IoT).To install the device explorer tool, see How to use the Device Explorer for IoT Hub devices.

Durante l'esecuzione del programma, viene visualizzata l'interfaccia seguente:When you run the program, you see this interface:

Immettere la stringa di connessione all'hub IoT nel primo campo e fare clic su Aggiorna.Enter your IoT Hub Connection String in the first field and click Update. Questo passaggio configura lo strumento per la comunicazione con l'hub IoT.This step configures the tool so that it can communicate with IoT Hub.

Dopo la configurazione della stringa di connessione all'hub IoT, fare clic sulla scheda Management (Gestione):When the IoT Hub connection string is configured, click the Management tab:

In questa scheda si gestiscono i dispositivi registrati nell'hub IoT.This tab is where you manage the devices registered in your IoT hub.

Per creare un dispositivo, fare clic sul pulsante Create (Crea).You create a device by clicking the Create button. Viene visualizzata una finestra di dialogo con un set di chiavi, primaria e secondaria, prepopolato.A dialog displays with a set of pre-populated keys (primary and secondary). Immettere un valore in Device ID (ID dispositivo) e fare clic su Create (Crea).Enter a Device ID and then click Create.

Dopo la creazione del dispositivo, l'elenco Devices (Dispositivi) viene aggiornato con tutti i dispositivi registrati, incluso quello appena creato.When the device is created, the Devices list updates with all the registered devices, including the one you just created. Facendo clic con il pulsante destro del mouse sul nuovo dispositivo, viene visualizzato questo menu:If you right-click your new device, you see this menu:

Se si sceglie Copy connection string for selected device (Copia stringa di connessione per il dispositivo selezionato), la stringa di connessione del dispositivo viene copiata negli Appunti.If you choose Copy connection string for selected device, the device connection string is copied to the clipboard. Conservare una copia della stringa di connessione del dispositivo.Keep a copy of the device connection string. È necessaria quando si eseguono le applicazioni di esempio descritte nelle sezioni seguenti.You need it when running the sample applications described in the following sections.

Dopo aver completato i passaggi precedenti, si è pronti per iniziare a eseguire il codice.When you've completed the steps above, you're ready to start running some code. Entrambi gli esempi hanno una costante all'inizio del file di origine principale che consente di immettere una stringa di connessione.Both samples have a constant at the top of the main source file that enables you to enter a connection string. Ecco, ad esempio, la stringa di connessione dell'applicazione iothub_client_sample_mqtt.For example, the corresponding line from the iothub_client_sample_mqtt application appears as follows.

static const char* connectionString = "[device connection string]";

Usare la libreria IoTHubClientUse the IoTHubClient library

Nella cartella iothub_client del repository azure-iot-sdk-c è presente una cartella samples che contiene un'applicazione denominata iothub_client_sample_mqtt.Within the iothub_client folder in the azure-iot-sdk-c repository, there is a samples folder that contains an application called iothub_client_sample_mqtt.

La versione per Windows dell'applicazione iothub_client_sample_mqtt include la soluzione di Visual Studio seguente:The Windows version of the iothub_client_sample_mqtt application includes the following Visual Studio solution:

Nota

Se si apre questo progetto in Visual Studio 2017, accettare la richiesta di ridestinare il progetto alla versione più recente.If you open this project in Visual Studio 2017, accept the prompts to retarget the project to the latest version.

Questa soluzione contiene un singolo progetto.This solution contains a single project. In questa soluzione sono installati quattro pacchetti NuGet:There are four NuGet packages installed in this solution:

  • Microsoft.Azure.C.SharedUtilityMicrosoft.Azure.C.SharedUtility
  • Microsoft.Azure.IoTHub.MqttTransportMicrosoft.Azure.IoTHub.MqttTransport
  • Microsoft.Azure.IoTHub.IoTHubClientMicrosoft.Azure.IoTHub.IoTHubClient
  • Microsoft.Azure.umqttMicrosoft.Azure.umqtt

Il pacchetto Microsoft.Azure.C.SharedUtility è sempre necessario quando si usa l'SDK.You always need the Microsoft.Azure.C.SharedUtility package when you are working with the SDK. Questo esempio usa il protocollo MQTT, quindi è necessario includere i pacchetti Microsoft.Azure.umqtt e Microsoft.Azure.IoTHub.MqttTransport. Sono disponibili pacchetti equivalenti per AMQP e HTTPS.This sample uses the MQTT protocol, therefore you must include the Microsoft.Azure.umqtt and Microsoft.Azure.IoTHub.MqttTransport packages (there are equivalent packages for AMQP and HTTPS). L'esempio usa la libreria IoTHubClient, quindi è necessario includere il pacchetto Microsoft.Azure.IoTHub.IoTHubClient nella soluzione.Because the sample uses the IoTHubClient library, you must also include the Microsoft.Azure.IoTHub.IoTHubClient package in your solution.

L'implementazione dell'applicazione di esempio si trova nel file di origine iothub_client_sample_mqtt.c.You can find the implementation for the sample application in the iothub_client_sample_mqtt.c source file.

I passaggi seguenti usano questa applicazione di esempio per illustrare i requisiti per l'uso della libreria IoTHubClient.The following steps use this sample application to walk you through what's required to use the IoTHubClient library.

Inizializzare la libreriaInitialize the library

Nota

Prima di iniziare a usare le librerie, è necessario eseguire alcune operazioni di inizializzazione specifiche della piattaforma.Before you start working with the libraries, you may need to perform some platform-specific initialization. Ad esempio, se si prevede di usare AMQP in Linux è necessario inizializzare la libreria OpenSSL.For example, if you plan to use AMQP on Linux you must initialize the OpenSSL library. Negli esempi del repository GitHub viene chiamata la funzione di utilità platform_init all'avvio del client e la funzione platform_deinit prima dell'uscita.The samples in the GitHub repository call the utility function platform_init when the client starts and call the platform_deinit function before exiting. Queste funzioni sono dichiarate nel file di intestazione platform.h.These functions are declared in the platform.h header file. Esaminare le definizioni di queste funzioni per la piattaforma di destinazione nel repository per determinare se è necessario includere un codice di inizializzazione specifico della piattaforma nel client.Examine the definitions of these functions for your target platform in the repository to determine whether you need to include any platform-specific initialization code in your client.

Per iniziare a lavorare con le librerie, allocare prima di tutto un handle del client per l'hub IoT:To start working with the libraries, first allocate an IoT Hub client handle:

if ((iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, MQTT_Protocol)) == NULL)
{
    (void)printf("ERROR: iotHubClientHandle is NULL!\r\n");
}
else
{
    ...

Si passa una copia della stringa di connessione del dispositivo ottenuta dallo strumento Device Explorer a questa funzione.You pass a copy of the device connection string you obtained from the device explorer tool to this function. Si stabilisce anche il protocollo da usare per le comunicazioni.You also designate the communications protocol to use. In questo esempio si usa MQTT, ma è possibile usare anche AMQP e HTTPS.This example uses MQTT, but AMQP and HTTPS are also options.

Quando è disponibile uno IOTHUB_CLIENT_HANDLE valido, si potrà iniziare a chiamare le API per l'invio e la ricezione di messaggi nell'hub IoT.When you have a valid IOTHUB_CLIENT_HANDLE, you can start calling the APIs to send and receive messages to and from IoT Hub.

Inviare messaggiSend messages

L'applicazione di esempio configura un ciclo per inviare messaggi all'hub IoT.The sample application sets up a loop to send messages to your IoT hub. Il frammento di codice seguente:The following snippet:

  • Crea un messaggio.Creates a message.
  • Aggiunge una proprietà al messaggio.Adds a property to the message.
  • Invia un messaggio.Sends a message.

Prima di tutto, creare un messaggio:First, create a message:

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);

Ogni volta che si invia un messaggio, si specifica un riferimento a una funzione di callback che viene richiamata quando i dati vengono inviati.Every time you send a message, you specify a reference to a callback function that's invoked when the data is sent. In questo esempio la funzione di callback è denominata SendConfirmationCallback.In this example, the callback function is called SendConfirmationCallback. Il frammento seguente illustra questa funzione di callback:The following snippet shows this callback function:

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, ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result));
    /* Some device specific action code goes here... */
    callbackCounter++;
    IoTHubMessage_Destroy(eventInstance->messageHandle);
}

Si noti la chiamata alla funzione IoTHubMessage_Destroy dopo il completamento dell'invio del messaggio.Note the call to the IoTHubMessage_Destroy function when you're done with the message. Questa funzione libera le risorse allocate quando si crea il messaggio.This function frees the resources allocated when you created the message.

Ricevere messaggiReceive messages

La ricezione di un messaggio è un'operazione asincrona.Receiving a message is an asynchronous operation. Prima di tutto registrare il callback da richiamare quando il dispositivo riceve un messaggio:First, you register the callback to invoke when the device receives a message:

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");
...

L'ultimo parametro è un puntatore nullo a qualsiasi elemento.The last parameter is a void pointer to whatever you want. Nell'esempio è un puntatore a un intero, ma potrebbe essere un puntatore a una struttura dei dati più complessa.In the sample, it's a pointer to an integer but it could be a pointer to a more complex data structure. Questo parametro consente alla funzione di callback di operare in uno stato condiviso con il chiamante della funzione.This parameter enables the callback function to operate on shared state with the caller of this function.

Quando il dispositivo riceve un messaggio, viene richiamata la funzione di callback registrata.When the device receives a message, the registered callback function is invoked. Questa funzione di callback recupera:This callback function retrieves:

  • L'ID messaggio e l'ID correlazione dal messaggio.The message id and correlation id from the message.
  • Il contenuto del messaggio.The message content.
  • Eventuali proprietà personalizzate dal messaggio.Any custom properties from the message.
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 = "<null>";
    }

    if ((correlationId = IoTHubMessage_GetCorrelationId(message)) == NULL)
    {
        correlationId = "<null>";
    }

    // 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: <<<%.*s>>> & 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;
}

Usare la funzione IoTHubMessage_GetByteArray per recuperare il messaggio, che in questo esempio è una stringa.Use the IoTHubMessage_GetByteArray function to retrieve the message, which in this example is a string.

Annullare l'inizializzazione della libreriaUninitialize the library

Al termine dell'invio degli eventi e della ricezione dei messaggi, è possibile annullare l'inizializzazione della libreria IoT.When you're done sending events and receiving messages, you can uninitialize the IoT library. A questo scopo, effettuare la chiamata di funzione seguente:To do so, issue the following function call:

IoTHubClient_LL_Destroy(iotHubClientHandle);

Questa chiamata libera le risorse allocate in precedenza dalla funzione IoTHubClient_CreateFromConnectionString.This call frees up the resources previously allocated by the IoTHubClient_CreateFromConnectionString function.

Come si può vedere, è facile inviare e ricevere messaggi con la libreria IoTHubClient.As you can see, it's easy to send and receive messages with the IoTHubClient library. La libreria gestisce i dettagli della comunicazione con l'hub IoT, ad esempio il protocollo da usare. Dal punto di vista dello sviluppatore, questa è una semplice opzione di configurazione.The library handles the details of communicating with IoT Hub, including which protocol to use (from the perspective of the developer, this is a simple configuration option).

La libreria IoTHubClient offre anche un controllo preciso della modalità di serializzazione dei dati che il dispositivo invia all'hub IoT.The IoTHubClient library also provides precise control over how to serialize the data your device sends to IoT Hub. In alcuni casi questo livello di controllo rappresenta un vantaggio, ma in altri è un dettaglio di implementazione di cui si preferirebbe non occuparsi.In some cases this level of control is an advantage, but in others it is an implementation detail that you don't want to be concerned with. In questo caso, è possibile considerare l'uso della libreria serializer, che viene illustrata nella sezione successiva.If that's the case, you might consider using the serializer library, which is described in the next section.

Usare la libreria serializerUse the serializer library

A livello concettuale, la libreria serializer si basa sulla libreria IoTHubClient nell'SDK.Conceptually the serializer library sits on top of the IoTHubClient library in the SDK. Usa la libreria IoTHubClient per la comunicazione sottostante con l'hub IoT, ma aggiunge anche funzionalità di modellazione che consentono allo sviluppatore di evitare l'impegno di doversi occupare della serializzazione dei messaggi.It uses the IoTHubClient library for the underlying communication with IoT Hub, but it adds modeling capabilities that remove the burden of dealing with message serialization from the developer. Il funzionamento di questa libreria può essere illustrato meglio con un esempio.How this library works is best demonstrated by an example.

Nella cartella serializer del repository azure-iot-sdk-c è presente una cartella samples che contiene un'applicazione denominata simplesample_mqtt.Inside the serializer folder in the azure-iot-sdk-c repository, is a samples folder that contains an application called simplesample_mqtt. La versione per Windows di questo esempio include la soluzione di Visual Studio seguente:The Windows version of this sample includes the following Visual Studio solution:

Nota

Se si apre questo progetto in Visual Studio 2017, accettare la richiesta di ridestinare il progetto alla versione più recente.If you open this project in Visual Studio 2017, accept the prompts to retarget the project to the latest version.

Come con l'esempio precedente, questo include diversi pacchetti NuGet:As with the previous sample, this one includes several NuGet packages:

  • Microsoft.Azure.C.SharedUtilityMicrosoft.Azure.C.SharedUtility
  • Microsoft.Azure.IoTHub.MqttTransportMicrosoft.Azure.IoTHub.MqttTransport
  • Microsoft.Azure.IoTHub.IoTHubClientMicrosoft.Azure.IoTHub.IoTHubClient
  • Microsoft.Azure.IoTHub.SerializerMicrosoft.Azure.IoTHub.Serializer
  • Microsoft.Azure.umqttMicrosoft.Azure.umqtt

La maggior parte di questi pacchetti è stata illustrata nell'esempio precedente, ma Microsoft.Azure.IoTHub.Serializer è nuovo.You've seen most of these packages in the previous sample, but Microsoft.Azure.IoTHub.Serializer is new. Questo pacchetto è necessario quando si usa la libreria serializer.This package is required when you use the serializer library.

L'implementazione dell'applicazione di esempio si trova nel file simplesample_mqtt.c.You can find the implementation of the sample application in the simplesample_mqtt.c file.

Le sezioni seguenti illustrano gli elementi chiave di questo esempio.The following sections walk you through the key parts of this sample.

Inizializzare la libreriaInitialize the library

Per iniziare a lavorare con la libreria serializer, chiamare le API di inizializzazione:To start working with the serializer library, call the initialization APIs:

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
        {
...

La chiamata alla funzione serializer_init è una chiamata singola e inizializza la libreria sottostante.The call to the serializer_init function is a one-time call and initializes the underlying library. Si chiamerà quindi la funzione IoTHubClient_LL_CreateFromConnectionString, che è la stessa API presente nell'esempio IoTHubClient.Then, you call the IoTHubClient_LL_CreateFromConnectionString function, which is the same API as in the IoTHubClient sample. Questa chiamata imposta la stringa di connessione del dispositivo e consente anche di scegliere il protocollo da usare.This call sets your device connection string (this call is also where you choose the protocol you want to use). Questo esempio usa MQTT come trasporto, ma potrebbe usare AMQP o HTTPS.This sample uses MQTT as the transport, but could use AMQP or HTTPS.

Chiamare infine la funzione CREATE_MODEL_INSTANCE.Finally, call the CREATE_MODEL_INSTANCE function. WeatherStation è lo spazio dei nomi del modello e ContosoAnemometer è il nome del modello.WeatherStation is the namespace of the model and ContosoAnemometer is the name of the model. Dopo la creazione dell'istanza del modello, è possibile usarla per iniziare a inviare e ricevere messaggi.Once the model instance is created, you can use it to start sending and receiving messages. Prima è però importante comprendere che cos'è un modello.However, it's important to understand what a model is.

Definire il modelloDefine the model

Un modello nella libreria serializer definisce i messaggi che il dispositivo può inviare all'hub IoT e i messaggi, chiamati azioni nel linguaggio di modellazione, che può ricevere.A model in the serializer library defines the messages that your device can send to IoT Hub and the messages, called actions in the modeling language, which it can receive. Per definire un modello, usare un set di macro C come nell'applicazione di esempio simplesample_mqtt:You define a model using a set of C macros as in the simplesample_mqtt sample application:

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);

Le macro BEGIN_NAMESPACE ed END_NAMESPACE accettano entrambe lo spazio dei nomi del modello come argomento.The BEGIN_NAMESPACE and END_NAMESPACE macros both take the namespace of the model as an argument. Tutto ciò che si trova tra queste macro è la definizione del modello o dei modelli e le strutture dei dati usate dal modello.It's expected that anything between these macros is the definition of your model or models, and the data structures that the models use.

In questo esempio è presente un singolo modello chiamato ContosoAnemometer.In this example, there is a single model called ContosoAnemometer. Questo modello definisce due parti di dati che il dispositivo può inviare all'hub IoT, ovvero DeviceId e WindSpeed.This model defines two pieces of data that your device can send to IoT Hub: DeviceId and WindSpeed. Definisce anche tre azioni (messaggi) che il dispositivo può ricevere, cioè TurnFanOn, TurnFanOff e SetAirResistance.It also defines three actions (messages) that your device can receive: TurnFanOn, TurnFanOff, and SetAirResistance. Ogni elemento dati ha un tipo e ogni azione un nome e facoltativamente un set di parametri.Each data element has a type, and each action has a name (and optionally a set of parameters).

I dati e le azioni specificati nel modello definiscono una superficie dell'API che si può usare per inviare messaggi all'hub IoT e per rispondere ai messaggi inviati al dispositivo.The data and actions defined in the model define an API surface that you can use to send messages to IoT Hub, and respond to messages sent to the device. L'uso di questo modello può essere illustrato meglio con un esempio.Use of this model is best understood through an example.

Inviare messaggiSend messages

Il modello definisce i dati che è possibile inviare all'hub IoT.The model defines the data you can send to IoT Hub. In questo esempio è uno dei due elementi dati definiti con la macro WITH_DATA.In this example, that means one of the two data items defined using the WITH_DATA macro. Sono necessari diversi passaggi per inviare i valori DeviceId e WindSpeed a un hub IoT.There are several steps required to send DeviceId and WindSpeed values to an IoT hub. Il primo consiste nell'impostare i dati da inviare:The first is to set the data you want to send:

myWeather->DeviceId = "myFirstDevice";
myWeather->WindSpeed = avgWindSpeed + (rand() % 4 + 2);

Il modello definito prima consente di impostare i valori impostando i membri di una struct.The model you defined earlier enables you to set the values by setting members of a struct. Serializzare quindi il messaggio che si vuole inviare:Next, serialize the message you want to send:

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);
}

Questo codice serializza il passaggio da dispositivo a cloud in un buffer, detto destination.This code serializes the device-to-cloud to a buffer (referenced by destination). Il codice richiama quindi la funzione sendMessage per inviare il messaggio all'hub IoT:The code then invokes the sendMessage function to send the message to IoT Hub:

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++;
}

Il penultimo parametro di IoTHubClient_LL_SendEventAsync è un riferimento a una funzione di callback chiamata dopo che l'invio dei dati è riuscito.The second to last parameter of IoTHubClient_LL_SendEventAsync is a reference to a callback function that's called when the data is successfully sent. Ecco la funzione di callback nell'esempio:Here's the callback function in the sample:

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", ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result));
}

Il secondo parametro è un puntatore al contesto utente, lo stesso puntatore passato a IoTHubClient_LL_SendEventAsync.The second parameter is a pointer to user context; the same pointer passed to IoTHubClient_LL_SendEventAsync. In questo caso, il contesto è un semplice contatore, ma potrebbe essere qualsiasi altro elemento.In this case, the context is a simple counter, but it can be anything you want.

Non sono necessarie altre operazioni per l'invio di messaggi da dispositivo a cloud.That's all there is to sending device-to-cloud messages. Rimane solo da illustrare come ricevere i messaggi.The only thing left to cover is how to receive messages.

Ricevere messaggiReceive messages

La ricezione di un messaggi funziona in modo simile alla modalità d'uso dei messaggi nella libreria IoTHubClient .Receiving a message works similarly to the way messages work in the IoTHubClient library. Prima di tutto occorre registrare la funzione di callback di un messaggio.First, you register a message callback function:

if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, IoTHubMessage, myWeather) != IOTHUB_CLIENT_OK)
{
    printf("unable to IoTHubClient_SetMessageCallback\r\n");
}
else
{
...

Si scrive quindi la funzione di callback che viene richiamata alla ricezione di un messaggio:Then, you write the callback function that's invoked when a message is received:

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;
}

Questo codice è un boilerplate, ovvero è lo stesso per qualsiasi soluzione.This code is boilerplate -- it's the same for any solution. La funzione riceve il messaggio e si occupa del routing alla funzione appropriata tramite la chiamata a EXECUTE_COMMAND.This function receives the message and takes care of routing it to the appropriate function through the call to EXECUTE_COMMAND. La funzione chiamata a questo punto dipende della definizione delle azioni nel modello.The function called at this point depends on the definition of the actions in your model.

Quando si definisce un'azione nel modello, viene richiesto di implementare una funzione che viene chiamata quando il dispositivo riceve il messaggio corrispondente.When you define an action in your model, you're required to implement a function that's called when your device receives the corresponding message. Ad esempio, se il modello definisce questa azione:For example, if your model defines this action:

WITH_ACTION(SetAirResistance, int, Position)

Definire una funzione con questa firma:Define a function with this signature:

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;
}

Si noti come il nome della funzione corrisponda al nome dell'azione nel modello e che i parametri della funzione corrispondono a quelli specificati per l'azione.Note how the name of the function matches the name of the action in the model and that the parameters of the function match the parameters specified for the action. Il primo parametro è sempre obbligatorio e contiene un puntatore all'istanza del modello.The first parameter is always required and contains a pointer to the instance of your model.

Quando il dispositivo riceve un messaggio che corrisponde alla firma, viene chiamata la funzione corrispondente.When the device receives a message that matches this signature, the corresponding function is called. Quindi, a parte la necessità di includere il codice boilerplate da IoTHubMessage, per la ricezione dei messaggi si tratta solo di definire una semplice funzione per ogni azione definita nel modello.Therefore, aside from having to include the boilerplate code from IoTHubMessage, receiving messages is just a matter of defining a simple function for each action defined in your model.

Annullare l'inizializzazione della libreriaUninitialize the library

Dopo avere completato l'invio dei dati e la ricezione di messaggi, è possibile annullare l'inizializzazione della libreria IoT:When you're done sending data and receiving messages, you can uninitialize the IoT library:

...
        DESTROY_MODEL_INSTANCE(myWeather);
    }
    IoTHubClient_LL_Destroy(iotHubClientHandle);
}
serializer_deinit();

Ognuna di queste tre funzioni è allineata con le tre funzioni di inizializzazione descritte in precedenza.Each of these three functions aligns with the three initialization functions described previously. Chiamando questi API si assicura che siano liberate le risorse allocate in precedenza.Calling these APIs ensures that you free previously allocated resources.

Passaggi successiviNext Steps

Questo articolo descrive le nozioni di base relative all'uso delle librerie in Azure IoT SDK per dispositivi per C. Fornisce informazioni sufficienti per comprendere il contenuto dell'SDK, la relativa architettura e come iniziare a usare gli esempi per Windows.This article covered the basics of using the libraries in the Azure IoT device SDK for C. It provided you with enough information to understand what's included in the SDK, its architecture, and how to get started working with the Windows samples. L'articolo successivo continua la descrizione dell'SDK, fornendo altre informazioni sulla libreria IoTHubClient.The next article continues the description of the SDK by explaining more about the IoTHubClient library.

Per altre informazioni sullo sviluppo dell'hub IoT, vedere gli Azure IoT SDK.To learn more about developing for IoT Hub, see the Azure IoT SDKs.

Per altre informazioni sulle funzionalità dell'hub IoT, vedere:To further explore the capabilities of IoT Hub, see: