Revision 1.7 03/16/2016
Revision | Updated By | Date | Major updates |
---|---|---|---|
0.1 | TA | 01/15/2015 | Initial draft |
0.2 | TA | 01/22/2015 | Reviewed comments and simplified API |
0.3 | TA | 01/30/2015 | Completed API for both the IOTHUB_message & IOTHUB_client |
1.0 | TA | 03/05/2015 | Updated based on the final implementation of the IOTHUB_client APIs |
1.1 | TE | 04/14/2015 | Adjust the config to use a function pointer rather than an enum |
1.2 | DC | 04/19/2015 | Rename DeviceHub to IoTHub |
1.3 | AP | 05/18/2015 | Rework to bring _DoWork and threadlessness. |
1.5 | AP | 07/02/2015 | Add _SetOption |
1.6 | TE | 09/24/2015 | Rework to bring up to date with current API |
1.7 | DR | 03/16/2016 | Adding ability to create multi-device transport. |
1.8 | AP | 06/30/2016 | Added x509 options and convert to .md |
1.9 | v-royspr | 03/28/2018 | Added timeout for blob transfer |
The IoTHub client "C" library offers developers a means of communication to & from an IoT Hub.
The library allows a device to achieve the supported communication patterns by a IoT Hub:
- Event. A message sent by a device to a IoT Hub. The device does not expect a response from IoT Hub, but the event message can result in errors (e.g. not being processed...).
- Message. A message sent by IoT Hub to the device. IoT Hub does not expect a response from the device, but the message can result in errors (e.g. failed delivery...)
The library offers the following features:
- The library provides an encapsulation for the message transfer between a device & IoT Hub.
- The library supports AMQP, HTTP, and MQTT protocols for communication with a IoT Hub.
- The library is a static lib.
- The source code for the library will be available under the "IOTHUB_client" under the "Azure/azure-iot-sdks" in the GitHub repository.
- The repository is located at: https://github.com/Azure/azure-iot-sdks
The APIs of this library cannot be called from different threads on the same handle without risking data races. Therefore, should more than 1 thread need to access concurrently the APIs of this module on the same handle, there needs to be a user-level synchronization mechanism that guarantees that two APIs are not called at the same time.
#include <stdio.h>
#include <stdlib.h>
#include "iothub_client.h"
#include "iothub_message.h"
#include "threadapi.h"
#include "crt_abstractions.h"
#include "iothubtransportamqp.h"
#define MAX_NUMBER_OF_MESSAGES 5
static const unsigned int SLEEP_IN_MILLISECONDS = 500;
static const int MESSAGE_BASE_TRACKING_ID = 42;
static int g_callbackInvoked;
typedef struct EVENT_INSTANCE_TAG
{
IOTHUB_MESSAGE_HANDLE messageHandle;
int messageTrackingId; // For tracking the messages within the user callback.
} EVENT_INSTANCE;
static void ReceiveConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback)
{
EVENT_INSTANCE* eventInstance = (EVENT_INSTANCE*)userContextCallback;
(void)printf("Confirmation[%d] received for message tracking id = %d with result = %d\r\n", g_callbackInvoked, eventInstance->messageTrackingId, result);
/* Some device specific action code goes here... */
g_callbackInvoked++;
}
int main(void)
{
const char* connectionString = "[device connection string]";
IOTHUB_CLIENT_HANDLE iotHubClientHandle;
EVENT_INSTANCE eventInstance[MAX_NUMBER_OF_MESSAGES];
size_t msgLength = 1024;
const char* msgText = malloc(msgLength);
g_callbackInvoked = 0;
(void)printf("Starting the IoTHub client sample to Send Event Asynchronously...\r\n");
if ((iotHubClientHandle = IoTHubClient_CreateFromConnectionString(connectionString, AMQP_Protocol)) == NULL)
{
(void)printf("ERROR: iotHubClientHandle is NULL!\r\n");
}
else
{
IOTHUB_CLIENT_STATUS sendStatus;
for (int i = 0; i < MAX_NUMBER_OF_MESSAGES; i++)
{
sprintf_s((char*)msgText, msgLength, "Message_%d_From_IoTHubClient", i);
if ((eventInstance[i].messageHandle = IoTHubMessage_CreateFromByteArray((const unsigned char*)msgText, strlen(msgText))) == NULL)
{
(void)printf("ERROR: IoTHubMessageHandle is NULL!\r\n");
}
else
{
eventInstance[i].messageTrackingId = MESSAGE_BASE_TRACKING_ID + i;
if (IoTHubClient_SendEventAsync(iotHubClientHandle, eventInstance[i].messageHandle, ReceiveConfirmationCallback, &eventInstance[i]) != IOTHUB_CLIENT_OK)
{
(void)printf("ERROR: IoTHubClient_SendEventAsync..........FAILED!\r\n");
}
else
{
(void)printf("IoTHubClient_SendEventAsync accepted data for transmission to IoT Hub.\r\n");
}
(void)printf("IoTHubClient_GetSendStatus() returns %d\r\n", IoTHubClient_GetSendStatus(iotHubClientHandle,&sendStatus));
(void)printf("IoTHubClient_GetSendStatus indicated a client status of: %d\r\n", sendStatus);
}
}
while (g_callbackInvoked < MAX_NUMBER_OF_MESSAGES)
{
ThreadAPI_Sleep(SLEEP_IN_MILLISECONDS);
}
(void)printf("IoTHubClient_GetSendStatus() returns %d\r\n", IoTHubClient_GetSendStatus(iotHubClientHandle, &sendStatus));
(void)printf("IoTHubClient_GetSendStatus indicated a client status of: %d\r\n", sendStatus);
}
IoTHubClient_Destroy(iotHubClientHandle);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include "iothub_client.h"
#include "iothub_message.h"
#include "threadapi.h"
#include "crt_abstractions.h"
#include "iothubtransportamqp.h"
#define MAX_NUMBER_OF_MESSAGES 100
static const unsigned int SLEEP_IN_MILLISECONDS = 500;
static const int MESSAGE_BASE_TRACKING_ID = 42;
static int g_callbackInvoked;
static IOTHUBMESSAGE_DISPOSITION_RESULT ReceiveMessageCallback(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback)
{
int* callbackInvoked = (int*)userContextCallback;
const unsigned char* payload;
size_t size;
if (IoTHubMessage_GetByteArray(message,&payload,&size) == IOTHUB_MESSAGE_OK)
{
(void)printf("Received message [%d] with Data: <<<%.*s>>> & Size=%d\r\n", *callbackInvoked, (int)size, payload, (int)size);
/* Some device specific action code goes here... */
}
else
{
(void)printf("Received message [%d] with bad data\r\n",*callbackInvoked);
}
(*callbackInvoked)++;
return IOTHUBMESSAGE_ACCEPTED;
}
int main(void)
{
const char* connectionString = "[device connection string]";
IOTHUB_CLIENT_HANDLE iotHubClientHandle;
g_callbackInvoked = 0;
(void)printf("Starting the IoTHub client sample to Receive messages...\r\n");
if ((iotHubClientHandle = IoTHubClient_CreateFromConnectionString(connectionString, AMQP_Protocol)) == NULL)
{
(void)printf("ERROR: iotHubClientHandle is NULL!\r\n");
}
else
{
if (IoTHubClient_SetMessageCallback(iotHubClientHandle, ReceiveMessageCallback, &g_callbackInvoked) != IOTHUB_CLIENT_OK)
{
(void)printf("ERROR: IoTHubClient_SetMessageCallback..........FAILED!\r\n");
}
else
{
time_t theTime;
(void)printf("IoTHubClient_SetMessageCallback...successful.\r\n");
while (g_callbackInvoked < MAX_NUMBER_OF_MESSAGES)
{
ThreadAPI_Sleep(SLEEP_IN_MILLISECONDS);
}
if(IoTHubClient_GetLastMessageReceiveTime(iotHubClientHandle, &theTime)!=IOTHUB_CLIENT_OK)
{
printf("unable to provide the last received message time...\r\n");
}
else
{
printf("the time when the last message was received was %s\r\n", ctime(&theTime));
}
}
}
IoTHubClient_Destroy(iotHubClientHandle);
return 0;
}
#define IOTHUB_CLIENT_RESULT_VALUES \
IOTHUB_CLIENT_OK, \
IOTHUB_CLIENT_INVALID_ARG, \
IOTHUB_CLIENT_ERROR, \
IOTHUB_CLIENT_INVALID_SIZE, \
IOTHUB_CLIENT_INDEFINITE_TIME \
DEFINE_ENUM(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_RESULT_VALUES);
#define IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES \
IOTHUB_CLIENT_CONFIRMATION_OK, \
IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY, \
IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT, \
IOTHUB_CLIENT_CONFIRMATION_ERROR \
DEFINE_ENUM(IOTHUB_CLIENT_CONFIRMATION_RESULT, IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES);
#define IOTHUB_CLIENT_STATUS_VALUES \
IOTHUB_CLIENT_SEND_STATUS_IDLE, \
IOTHUB_CLIENT_SEND_STATUS_BUSY \
DEFINE_ENUM(IOTHUB_CLIENT_STATUS, IOTHUB_CLIENT_STATUS_VALUES);
#define IOTHUBMESSAGE_DISPOSITION_RESULT_VALUES \
IOTHUBMESSAGE_ACCEPTED, \
IOTHUBMESSAGE_REJECTED, \
IOTHUBMESSAGE_ABANDONED, \
IOTHUBMESSAGE_ASYNC_ACK
DEFINE_ENUM(IOTHUBMESSAGE_DISPOSITION_RESULT, IOTHUBMESSAGE_DISPOSITION_RESULT_VALUES);
typedef void(*IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK)(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback);
typedef IOTHUBMESSAGE_DISPOSITION_RESULT (*IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC)(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback);
typedef const void*(*IOTHUB_CLIENT_TRANSPORT_PROVIDER)(void);
typedef struct IOTHUB_CLIENT_CONFIG_TAG
{
IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol;
const char* deviceId;
const char* deviceKey;
const char* iotHubName;
const char* iotHubSuffix;
const char* protocolGatewayHostName;
} IOTHUB_CLIENT_CONFIG;
typedef struct IOTHUB_CLIENT_DEVICE_CONFIG_TAG
{
IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol;
void * transportHandle;
const char* deviceId;
const char* deviceKey;
} IOTHUB_CLIENT_DEVICE_CONFIG;
typedef struct IOTHUBTRANSPORT_CONFIG_TAG
{
const IOTHUB_CLIENT_CONFIG* upperConfig;
PDLIST_ENTRY waitingToSend;
}IOTHUBTRANSPORT_CONFIG;
#define IOTHUB_MESSAGE_RESULT_VALUES \
IOTHUB_MESSAGE_OK, \
IOTHUB_MESSAGE_INVALID_ARG, \
IOTHUB_MESSAGE_INVALID_TYPE, \
IOTHUB_MESSAGE_ERROR \
DEFINE_ENUM(IOTHUB_MESSAGE_RESULT, IOTHUB_MESSAGE_RESULT_VALUES);
#define IOTHUBMESSAGE_CONTENT_TYPE_VALUES \
IOTHUBMESSAGE_BYTEARRAY, \
IOTHUBMESSAGE_STRING, \
IOTHUBMESSAGE_UNKNOWN \
DEFINE_ENUM(IOTHUBMESSAGE_CONTENT_TYPE, IOTHUBMESSAGE_CONTENT_TYPE_VALUES);
struct members
Name | Description |
---|---|
protocol | A function pointer that is passed into the IoTHubClientCreate. A function definition for amqp, AMQP_Protocol, is defined in the include iothubtransportamqp.h. A function definition for http, HTTP_Protocol, is defined in the include iothubtransporthttp.h. A function definition for mqtt, MQTT_Protocol, is defined in the include iothubtransportmqtt.h. |
deviceId | A string that identifies the device. |
deviceKey | The device key used to authenticate the device. |
iotHubName | The IoT Hub name to which the device is connecting. |
iotHubSuffix | The IoT Hub suffix goes here, e.g., azure-devices.net. |
protocolGatewayHostName | The address of the protocol gateway the client will use to connect to send data and receive messages. This is for protocols which are supported via a protocol gateway. For example, mqtt. |
struct members
Name | Description |
---|---|
protocol | A function pointer that is passed into the IoTHubClientCreate. A function definition for amqp, AMQP_Protocol, is defined in the include iothubtransportamqp.h. A function definition for http, HTTP_Protocol, is defined in the include iothubtransporthttp.h. A function definition for mqtt, MQTT_Protocol, is defined in the include iothubtransportmqtt.h. |
deviceId | A string that identifies the device. |
deviceKey | The device key used to authenticate the device. |
transportHandle | The transport connection to be used for this device. |
IOTHUB_CLIENT_HANDLE IoTHubClient_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol);
Creates a IoT Hub client for communication with an existing IoT Hub using the specified connection string parameter. The API does not allow sharing of a connection across multiple devices. This is a blocking call.
Sample connection string:
HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., azure-devices.net]; DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];
Name | Description |
---|---|
connectionString | Pointer to a character string |
protocol | Function pointer for protocol implementation |
- A Non-NULL handle value that is used when invoking other functions for IoT Hub client.
- NULL on failure.
Creates a IoT Hub client for communication with an existing IoT Hub using the specified parameters. The API does not allow sharing of a connection across multiple devices. This is a blocking call.
Name | Description |
---|---|
config | Pointer to a IOTHUB_CLIENT_CONFIG structure |
- A Non-NULL handle value that is used when invoking other functions for IoT Hub client.
- NULL on failure.
This API allows sharing of a connection across multiple devices. Creates a IoT Hub client for communication with an existing IoT Hub using the specified parameters. This is a blocking call.
Name | Description |
---|---|
config | Pointer to a IOTHUB_CLIENT_DEVICE_CONFIG structure |
- A Non-NULL handle value that is used when invoking other functions for IoT Hub client.
- NULL on failure.
Disposes of resources allocated by the IoT Hub client. Any pending events that have not yet been sent to the IoT Hub will be immediately completed with a IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY status. Other events that are actually out on the wire but not finished may receive an IOTHUB_CLIENT_CONFIRMATION_ERROR status. This is a blocking call.
Name | Description |
---|---|
iotHubClientHandle | The handle created by a call to the create function. |
- No return.
IOTHUB_CLIENT_RESULT IoTHubClient_SendEventAsync(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback);
Asynchronous call to send the message specified by the IoTHubMessageHandle. NOTE: The application behavior is undefined if the user calls the IoTHubClient_Destroy from within any callback.
Name | Description |
---|---|
iotHubClientHandle | The handle created by a call to the create function. |
eventMessageHandle | The handle to a IoT Hub message. |
eventConfirmationCallBack | The callback specified by the device for receiving confirmation of the delivery of the IoT Hub message. This callback can be expected to invoke SendEventAsync for the same message in attempt to retry sending a failing message. The user can specify a NULL value here to indicate no callback required. |
userContextCallback | User specified context that will be provided to the callback. This can be NULL. |
- IOTHUB_CLIENT_OK upon success.
- Error code upon failure.
IOTHUB_CLIENT_RESULT IoTHubClient_SetMessageCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback);
Sets up the message callback invoked when IoT Hub issues a message to the device. This is a blocking call. NOTE: The application behavior is undefined if the user calls the IoTHubClient_Destroy from within any callback.
Name | Description |
---|---|
iotHubClientHandle | The handle created by a call to the create function. |
messageCallback | The callback specified by the device for receiving messages from IoT Hub. |
userContextCallback | User specified context that will be provided to the callback. This can be NULL. |
- IOTHUB_CLIENT_OK upon success.
- Error code upon failure.
IOTHUB_CLIENT_RESULT IoTHubClient_GetLastMessageReceiveTime (IOTHUB_CLIENT_HANDLE iotHubClientHandle, time_t* lastMessageReceiveTime);
This function returns in the out parameter lastMessageReceiveTime what was the value of the time() function when the last notification was received at the client.
Name | Description |
---|---|
iotHubClientHandle | The handle created by a call to the create function. |
lastMessageReceiveTime | Out parameter containing the value of time() function when the last message was received |
- IOTHUB_CLIENT_OK upon success.
- Error code upon failure.
IOTHUB_CLIENT_RESULT IoTHubClient_GetSendStatus(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_STATUS* iotHubClientStatus);
This function returns the current sending status for IoTHubClient.
Name | Description |
---|---|
iotHubClientHandle | The handle created by a call to the create function. |
iotHubClientStatus | A pointer to an IOTHUB_CLIENT_STATUS. If the function call is successful then what is pointed to will receive: IOTHUBCLIENT_SENDSTATUS_IDLE if there are currently no items to be sent. IOTHUBCLIENT_SENDSTATUS_BUSY if there are currently items to be sent. |
- IOTHUB_CLIENT_OK upon success
- IOTHUBCLIENT _INVALID_ARG if called with NULL parameter
- Error code upon failure
Once a message is received from the service, if the user has set a callback, the receive callback shall be invoked. This call back is defined as:
typedef IOTHUBMESSAGE_DISPOSITION_RESULT
(*IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC)(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback);
If the callback returns the status IOTHUBMESSAGE_ACCEPTED, the client will accept the message, meaning that it will not be received again by the client. If the callback returns the status IOTHUBMESSAGE_REJECTED, the message will be rejected. The message will not be resent to the device. If the callback returns the status IOTHUBMESSAGE_ABANDONED, the message will be abandoned. This implies that the user could not process the message but it expected that the message will be resent to the device from the service. Message is only valid in the scope of the callback.
IOTHUBMESSAGE_ASYNC_ACK will allow the application to accept the message at a later time outside of the callback. The application must call IoTHubDeviceClient_LL_SendMessageDisposition() with the message disposition result. Please see iothub_ll_c2d_sample.c for implementation details.
IOTHUB_CLIENT_RESULT IoTHubClient_SetOption(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* optionName, const void* value);
IoTHubClient_SetOption shall set a runtime option identified by parameter optionName
to a value pointed to by parameter value. The optionName and the data type value is pointing to are specific for every option.
- "timeout" - the maximum time in miliseconds a communication is allowed to use. value is a pointer to an unsigned int with the meaning of "miliseconds". This is only supported for HTTP protocol so far. When the HTTP protocol uses CURL, the meaning of the parameter is total request time. When the HTTP protocol uses winhttp, the meaning is dwSendTimeout and dwReceiveTimeout parameters of WinHttpSetTimeouts API.
- "blob_upload_timeout_secs" - the maximum time in seconds allowed for a blob transfer. The value is a
pointer to a
size_t
. A value of 0 uses the default timeout for the underlying transport. - "CURLOPT_LOW_SPEED_LIMIT" - only available for HTTP protocol and only when CURL is used. It has the same meaning as CURL's option with the same name. value is pointer to a long.
- "CURLOPT_LOW_SPEED_TIME" - only available for HTTP protocol and only when CURL is used. It has the same meaning as CURL's option with the same name. value is pointer to a long.
- "CURLOPT_FORBID_REUSE" - only available for HTTP protocol and only when CURL is used. It has the same meaning as CURL's option with the same name. value is pointer to a long.
- "CURLOPT_FRESH_CONNECT" - only available for HTTP protocol and only when CURL is used. It has the same meaning as CURL's option with the same name. value is pointer to a long.
- "CURLOPT_VERBOSE" - only available for HTTP protocol and only when CURL is used. It has the same meaning as CURL's option with the same name. value is pointer to a long.
- "messageTimeout" - maximum transmission time for an event from the moment when _SendAsync is called. value is a pointer to a uint64_t that contains the number of miliseconds after which events timeout. When the event times out, the event callback is invoked and the code IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT is passed as parameter result.
By default messages never expire. The meaning of the messageTimeout value is the following:
- 0 = disable message timeout for all messages send by _SendAsync from now on
- Any other number - consider that number as the timeout.
- "x509certificate" - feeds a x509 certificate in PEM format to IoTHubClient to be used for authentication. value is a pointer to a null terminated string that contains the certificate. Important: certain TLS stacks are sensitive to line terminators. Example:
const char* value =
"-----BEGIN CERTIFICATE-----\n"
"MIICpDCCAYwCCQCfIjBnPxs5TzANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls\n"
"b2NhbGhvc3QwHhcNMTYwNjIyMjM0MzI3WhcNMTYwNjIzMjM0MzI3WjAUMRIwEAYD\n"
...
"+s88wBF907s1dcY45vsG0ldE3f7Y6anGF60nUwYao/fN/eb5FT5EHANVMmnK8zZ2\n"
"tjWUt5TFnAveFoQWIoIbtzlTbOxUFwMrQFzFXOrZoDJmHNWc2u6FmVAkowoOSHiE\n"
"dkyVdoGPCXc=\n"
"-----END CERTIFICATE-----\n";
- "x509privatekey" - feed a x509 private key in PEM format to IoTHubClient to be used for authentication. value is a pointer to a null terminated string that contains the key. Important: certain TLS stacks are sensitive to line terminators. Example:
const char* x509privatekey =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEpQIBAAKCAQEA0zKK+Uu5I0nXq2V6+2gbdCsBXZ6j1uAgU/clsCohEAek1T8v\n"
"qj2tR9Mz9iy9RtXPMHwzcQ7aXDaz7RbHdw7tYXqSw8iq0Mxq2s3p4mo6gd5vEOiN\n"
...
"EyePNmkCgYEAng+12qvs0de7OhkTjX9FLxluLWxfN2vbtQCWXslLCG+Es/ZzGlNF\n"
"SaqVID4EAUgUqFDw0UO6SKLT+HyFjOr5qdHkfAmRzwE/0RBN69g2qLDN3Km1Px/k\n"
"xyJyxc700uV1eKiCdRLRuCbUeecOSZreh8YRIQQXoG8uotO5IttdVRc=\n"
"-----END RSA PRIVATE KEY-----\n";
- "CipherSuite" - only available when OpenSSL is used. value is a pointer to a null terminated string that contains a list in the format specified by SSL_set_cipher_list. Example:
// IoT Hub using ECC server certificate chain:
IoTHubDeviceClient_LL_SetOption(device_ll_handle, OPTION_OPENSSL_CIPHER_SUITE, "ECDH+ECDSA+HIGH");
- "Engine" - only available when OpenSSL is used. It specifies the OpenSSL built-in engine to be loaded. value is a null terminated string that contains the engine name.
- "x509PrivatekeyType" - only available when OpenSSL is used and OPTION_OPENSSL_ENGINE is configured. value is a pointer to a long. When set to 0x1, the private key is loaded from the OpenSSL Engine. The
x509privatekey
option represents the engine-specific certificate identifier.
// Example using Azure IoT Identity, Key and Certificate Services (https://azure.github.io/iot-identity-service/)
const char* opensslEngine = "aziot_keys";
static const OPTION_OPENSSL_KEY_TYPE x509_key_from_engine = KEY_TYPE_ENGINE;
// Private KeyID obtained by querying Key Service. E.g.:
// curl --unix-socket /run/aziot/keyd.sock http://foo/keypair/device-id?api-version=2020-09-01
const char* x509privatekey = "sr=eyJrZXlfaWQiOnsiS2V5UGFpciI6ImRldmljZS...zGwtvKYW6dlkjtPK2ljVqUjmC9gqvZTmw=";
// Public certificate obtained by querying Certificate Service. E.g.:
// curl --unix-socket /run/aziot/certd.sock http://foo/certificates/device-id?api-version=2020-09-01
const char* x509certificate =
"-----BEGIN CERTIFICATE-----\n"
"MIIBJTCBywIUUennAV2WbZsckSIcMHLXuak/iCswCgYIKoZIzj0EAwIwFTETMBEG\n"
// [...]
"OkZcAK4VBLwYnoH+glXK6pWqDkXE0wIhAM0OFOTbVIuXOGDXaCKxFLIvMifo2RJZ\n"
"b5pjgB2gaGGi\n"
"-----END CERTIFICATE-----\n";
// Example using TPM TSS OpenSSL ENGINE (https://github.com/tpm2-software/tpm2-tss-engine)
const char* opensslEngine = "tpm2tss";
static const OPTION_OPENSSL_KEY_TYPE x509_key_from_engine = KEY_TYPE_ENGINE;
// Certificate and key-file obtained using tpm2tss-genkey:
const char* x509certificate =
"-----BEGIN CERTIFICATE-----\n"
"MIIBJTCBywIUUennAV2WbZsckSIcMHLXuak/iCswCgYIKoZIzj0EAwIwFTETMBEG\n"
// [...]
"OkZcAK4VBLwYnoH+glXK6pWqDkXE0wIhAM0OFOTbVIuXOGDXaCKxFLIvMifo2RJZ\n"
"b5pjgB2gaGGi\n"
"-----END CERTIFICATE-----\n";
// When the TPM2TSS engine is used, the key identifier is a path to a PEM-encoded TSS2 private key:
const char* x509privatekey = "/home/restricted_user/tpm2ec.tss"
// Example using PKCS#11 OpenSSL ENGINE (https://github.com/OpenSC/libp11)
// The OpenSSL ENGINE must be associated to a pkcs11 module within openssl.cnf.
static const char* opensslEngine = "pkcs11";
static const OPTION_OPENSSL_KEY_TYPE x509_key_from_engine = KEY_TYPE_ENGINE;
// Certificate can be extracted from the PKCS#11 library using pkcs11-tool from OpenSC.
static const char* x509certificate =
"-----BEGIN CERTIFICATE-----\n"
"MIIBMTCB1wIUTu66kxJIBR5t5IkAwh7Lqm/AM+IwCgYIKoZIzj0EAwIwGzEZMBcG\n"
// [...]
"DItkq1MHqzqExB1eTrMHQVY11w62\n"
"-----END CERTIFICATE-----\n";
// The private key contains the PKCS#11 URI.
static const char* x509privatekey = "pkcs11:object=ec-privkey;type=private?pin-value=1234";
IoTHubClient API surface also contains a separate set of APIs that allow the user to interact with the lower layer portion of the IoTHubClient. These APIs contain LL in their name, but retain the same functionality like the IoTHubClient_... APIs, with one difference. If the LL APIs are used the user is responsible for scheduling when the actual work done by the IoTHubClient happens (when the data is sent/received on/from the wire). This is useful for constrained devices where spinning a separate thread is often not desired.
This function is user called when work (sending/receiving) can be done by the IoTHubClient. All IoTHubClient interactions (in regards to network trafic and/or user level callbacks) are the effect of calling this function and they take place synchronously inside _DoWork.
Name | Description |
---|---|
IotHubClientHandle | The handle created by a call to the create function. |
#include <stdio.h>
#include <stdlib.h>
#include "iothub_client.h"
#include "iothub_message.h"
#include "threadapi.h"
#include "crt_abstractions.h"
#include "iothubtransportamqp.h"
#define MAX_NUMBER_OF_MESSAGES 5
static const unsigned int SLEEP_IN_MILLISECONDS = 500;
static const int MESSAGE_BASE_TRACKING_ID = 42;
static int g_callbackInvoked;
typedef struct EVENT_INSTANCE_TAG
{
IOTHUB_MESSAGE_HANDLE messageHandle;
int messageTrackingId; // For tracking the messages within the user callback.
} EVENT_INSTANCE;
static void MessageConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback)
{
EVENT_INSTANCE* eventInstance = (EVENT_INSTANCE*)userContextCallback;
(void)printf("Confirmation[%d] received for message tracking id = %d with result = %d\r\n", g_callbackInvoked, eventInstance->messageTrackingId, result);
/* Some device specific action code goes here... */
g_callbackInvoked++;
}
int main(void)
{
const char* connectionString = "[device connection string]";
IOTHUB_CLIENT_HANDLE iotHubClientHandle;
EVENT_INSTANCE eventInstance[MAX_NUMBER_OF_MESSAGES];
size_t msgLength = 1024;
const char* msgText = malloc(msgLength);
g_callbackInvoked = 0;
(void)printf("Starting the IoTHub client sample to Send Event Asynchronously...\r\n");
if ((iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, AMQP_Protocol)) == NULL)
{
(void)printf("ERROR: iotHubClientHandle is NULL!\r\n");
}
else
{
IOTHUB_CLIENT_RESULT sendResult,getResult;
IOTHUB_CLIENT_STATUS sendStatus;
for (int i = 0; i < MAX_NUMBER_OF_MESSAGES; i++)
{
sprintf_s((char*)msgText, msgLength, "Message_%d_From_IoTHubClient", i);
if ((eventInstance[i].messageHandle = IoTHubMessage_CreateFromString(msgText)) == NULL)
{
(void)printf("ERROR: IoTHubMessageHandle is NULL!\r\n");
}
else
{
eventInstance[i].messageTrackingId = MESSAGE_BASE_TRACKING_ID + i;
if ((sendResult = IoTHubClient_LL_SendEventAsync(iotHubClientHandle, eventInstance[i].messageHandle, MessageConfirmationCallback, &eventInstance[i])) != IOTHUB_CLIENT_OK)
{
(void)printf("ERROR: IoTHubClient_LL_SendEventAsync..........FAILED! Status is: %d\r\n",sendResult);
}
else
{
(void)printf("IoTHubClient_LL_SendEventAsync accepted data for transmission to IoT Hub.\r\n");
}
getResult = IoTHubClient_LL_GetSendStatus(iotHubClientHandle, &sendStatus);
if (getResult != IOTHUB_CLIENT_OK)
{
(void)printf("IoTHubClient_LL_GetSendStatus failed! status is: %d", getResult);
}
else
{
(void)printf("IoTHubClient_LL_GetSendStatus() returns %d\r\n", sendStatus);
}
}
}
while (g_callbackInvoked < MAX_NUMBER_OF_MESSAGES)
{
IoTHubClient_LL_DoWork(iotHubClientHandle);
getResult = IoTHubClient_LL_GetSendStatus(iotHubClientHandle, &sendStatus);
if (getResult != IOTHUB_CLIENT_OK)
{
(void)printf("IoTHubClient_LL_GetSendStatus failed! status is: %d", getResult);
}
else
{
(void)printf("IoTHubClient_LL_GetSendStatus() returns %d\r\n", sendStatus);
}
ThreadAPI_Sleep(SLEEP_IN_MILLISECONDS);
}
}
IoTHubClient_LL_Destroy(iotHubClientHandle);
return 0;
}
IOTHUB_MESSAGE_HANDLE IoTHubMessage_CreateFromByteArray(const unsigned char* byteArray, size_t size);
Creates an IoT Hub message to be used for operations between the device and IoT Hub. The data pointed to by byteArray may contain unprintable data and is not zero terminated.
Name | Description |
---|---|
byteArray | A pointer to data for the message. |
size | The number of unsigned chars pointed to by byteArray. If size is zero then byteArray may be NULL. If size is not zero then byteArray MUST NOT be NULL. |
- A None-NULL handle value that is used when invoking other functions for IoT Hub message.
- NULL on failure.
Creates an IoT Hub message to be used for operations between the device and IoT Hub. The data is assumed to be printable and is zero terminated.
Name | Description |
---|---|
source | A pointer to data for the message. |
- A non-NULL handle value that is used when invoking other functions for IoT Hub message.
- NULL on failure.
Creates a new IoT Hub message with the content identical to that of the iotHubMessageHandle parameter.
Name | Description |
---|---|
iotHubMessageHandle | Handle to the message to be cloned. |
- A non-NULL handle value that is used when invoking other functions for IoT Hub message.
- NULL on failure.
IOTHUB_MESSAGE_RESULT IoTHubMessage_GetByteArray(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const unsigned char** buffer, size_t* size);
Fetches a pointer and size for the data associated with the IoT Hub message handle. If the content type of the message is not IOTHUBMESSAGE_BYTEARRAY then the function returns IOTHUB_MESSAGE_INVALID_ARG.
Name | Description |
---|---|
iotHubMessageHandle | Handle to the message to be cloned. |
buffer | Pointer to the memory location where the pointer to the buffer will be written. |
size | The size of the buffer will be written to this address. |
- IOTHUB_MESSAGE_OK if the byte array was fetched successfully.
- Error code upon failure.
Returns the zero terminated string stored in message. If the content type of the message is not IOTHUBMESSAGE_STRING then the function returns NULL.
Name | Description |
---|---|
iotHubMessageHandle | Handle to the message. |
- Pointer to the zero terminated string stored in the message.
- NULL if an error occurs or the content type is incorrect.
Returns the content type of the message given by the parameter iotHubMessageHandle.
Name | Description |
---|---|
iotHubMessageHandle | Handle to the message. |
- IOTHUBMESSAGE_BYTEARRAY for a message created via IoTHubMessage_CreateFromByteArray
- IOTHUBMESSAGE_STRING for a message created via IoTHubMessage_CreateFromString
- IOTHUBMESSAGE_UNKNOWN otherwise.
Returns a handle to the message's properties map. Note that when sending messages via the HTTP transport, the key names in the map must not contain spaces.
Name | Description |
---|---|
iotHubMessageHandle | Handle to the message. |
MAP_HANDLE representing the message's property map.
Disposes of resources allocated by the IoT Hub message.
Name | Description |
---|---|
ioTHubMessageHandle | The handle created by a call to the create function. |
Return
- No return.