source/azure_iot_hub_client.c (1,146 lines of code) (raw):
/* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License. */
/**
* @file azure_iot_hub_client.c
* @brief Implementation of the Azure IoT Hub Client.
*/
#include "azure_iot_hub_client.h"
/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "azure_iot_mqtt.h"
#include "azure_iot_private.h"
#include "azure_iot_result.h"
#include "azure_iot_version.h"
/* Azure SDK for Embedded C includes */
#include "azure/az_iot.h"
#include "azure/core/az_version.h"
#ifndef azureiothubDEFAULT_TOKEN_TIMEOUT_IN_SEC
#define azureiothubDEFAULT_TOKEN_TIMEOUT_IN_SEC azureiotconfigDEFAULT_TOKEN_TIMEOUT_IN_SEC
#endif /* azureiothubDEFAULT_TOKEN_TIMEOUT_IN_SEC */
#ifndef azureiothubKEEP_ALIVE_TIMEOUT_SECONDS
#define azureiothubKEEP_ALIVE_TIMEOUT_SECONDS azureiotconfigKEEP_ALIVE_TIMEOUT_SECONDS
#endif /* azureiothubKEEP_ALIVE_TIMEOUT_SECONDS */
#ifndef azureiothubSUBACK_WAIT_INTERVAL_MS
#define azureiothubSUBACK_WAIT_INTERVAL_MS azureiotconfigSUBACK_WAIT_INTERVAL_MS
#endif /* azureiothubSUBACK_WAIT_INTERVAL_MS */
#ifndef azureiothubUSER_AGENT
#define azureiothubUSER_AGENT "c%2F" azureiotVERSION_STRING "%28FreeRTOS%29"
#endif /* azureiothubUSER_AGENT */
/*
* Topic subscribe state
*/
#define azureiothubTOPIC_SUBSCRIBE_STATE_NONE ( 0x0 )
#define azureiothubTOPIC_SUBSCRIBE_STATE_SUB ( 0x1 )
#define azureiothubTOPIC_SUBSCRIBE_STATE_SUBACK ( 0x2 )
/*
* Indexes of the receive context buffer for each feature
*/
#define azureiothubRECEIVE_CONTEXT_INDEX_C2D ( 0 )
#define azureiothubRECEIVE_CONTEXT_INDEX_COMMANDS ( 1 )
#define azureiothubRECEIVE_CONTEXT_INDEX_PROPERTIES ( 2 )
#define azureiothubCOMMAND_EMPTY_RESPONSE "{}"
#define azureiothubMAX_SIZE_FOR_UINT32 ( 10 )
#define azureiothubHMACBufferLength ( 48 )
/*-----------------------------------------------------------*/
/**
*
* Handle any incoming publish messages.
*
* */
static void prvMQTTProcessIncomingPublish( AzureIoTHubClient_t * pxAzureIoTHubClient,
AzureIoTMQTTPublishInfo_t * pxPublishInfo )
{
uint32_t ulIndex;
AzureIoTHubClientReceiveContext_t * pxContext;
configASSERT( pxPublishInfo != NULL );
if( ( pxPublishInfo->pcTopicName == NULL ) ||
( pxPublishInfo->usTopicNameLength == 0 ) )
{
AZLogWarn( ( "Ignoring processing of empty topic" ) );
return;
}
for( ulIndex = 0; ulIndex < azureiothubSUBSCRIBE_FEATURE_COUNT; ulIndex++ )
{
pxContext = &pxAzureIoTHubClient->_internal.xReceiveContext[ ulIndex ];
if( ( pxContext->_internal.pxProcessFunction != NULL ) &&
( pxContext->_internal.pxProcessFunction( pxContext,
pxAzureIoTHubClient,
( void * ) pxPublishInfo ) == eAzureIoTSuccess ) )
{
break;
}
}
/* If reached the end of the list and haven't found a context, log none found */
if( ulIndex == azureiothubSUBSCRIBE_FEATURE_COUNT )
{
AZLogInfo( ( "No receive context found for incoming publish on topic: %.*s",
pxPublishInfo->usTopicNameLength, pxPublishInfo->pcTopicName ) );
}
}
/*-----------------------------------------------------------*/
/**
*
* Handle any incoming suback messages.
*
* */
static void prvMQTTProcessSuback( AzureIoTHubClient_t * pxAzureIoTHubClient,
AzureIoTMQTTPacketInfo_t * pxIncomingPacket,
uint16_t usPacketID )
{
uint32_t ulIndex;
AzureIoTHubClientReceiveContext_t * pxContext;
( void ) pxIncomingPacket;
configASSERT( pxIncomingPacket != NULL );
configASSERT( ( azureiotmqttGET_PACKET_TYPE( pxIncomingPacket->ucType ) ) == azureiotmqttPACKET_TYPE_SUBACK );
for( ulIndex = 0; ulIndex < azureiothubSUBSCRIBE_FEATURE_COUNT; ulIndex++ )
{
pxContext = &pxAzureIoTHubClient->_internal.xReceiveContext[ ulIndex ];
if( pxContext->_internal.usMqttSubPacketID == usPacketID )
{
/* We assume success since IoT Hub would disconnect if there was a problem subscribing. */
pxContext->_internal.usState = azureiothubTOPIC_SUBSCRIBE_STATE_SUBACK;
AZLogInfo( ( "Suback receive context found: 0x%08x", ( uint16_t ) ulIndex ) );
break;
}
}
/* If reached the end of the list and haven't found a context, log none found */
if( ulIndex == azureiothubSUBSCRIBE_FEATURE_COUNT )
{
AZLogInfo( ( "No receive context found for incoming suback" ) );
}
}
/*-----------------------------------------------------------*/
/**
*
* Handle any incoming puback messages.
*
* */
static void prvMQTTProcessPuback( AzureIoTHubClient_t * pxAzureIoTHubClient,
AzureIoTMQTTPacketInfo_t * pxIncomingPacket,
uint16_t usPacketID )
{
( void ) pxIncomingPacket;
configASSERT( pxIncomingPacket != NULL );
configASSERT( ( azureiotmqttGET_PACKET_TYPE( pxIncomingPacket->ucType ) ) == azureiotmqttPACKET_TYPE_PUBACK );
AZLogInfo( ( "Puback received for packet id: 0x%08x", usPacketID ) );
if( pxAzureIoTHubClient->_internal.xTelemetryCallback != NULL )
{
AZLogDebug( ( "Invoking telemetry puback callback" ) );
pxAzureIoTHubClient->_internal.xTelemetryCallback( usPacketID );
AZLogDebug( ( "Returned from telemetry puback callback" ) );
}
}
/*-----------------------------------------------------------*/
/**
*
* Process incoming MQTT events.
*
* */
static void prvEventCallback( AzureIoTMQTTHandle_t pxMQTTContext,
AzureIoTMQTTPacketInfo_t * pxPacketInfo,
AzureIoTMQTTDeserializedInfo_t * pxDeserializedInfo )
{
/* First element in AzureIoTHubClientHandle */
AzureIoTHubClient_t * pxAzureIoTHubClient = ( AzureIoTHubClient_t * ) pxMQTTContext;
if( ( azureiotmqttGET_PACKET_TYPE( pxPacketInfo->ucType ) ) == azureiotmqttPACKET_TYPE_PUBLISH )
{
prvMQTTProcessIncomingPublish( pxAzureIoTHubClient, pxDeserializedInfo->pxPublishInfo );
}
else if( ( azureiotmqttGET_PACKET_TYPE( pxPacketInfo->ucType ) ) == azureiotmqttPACKET_TYPE_SUBACK )
{
prvMQTTProcessSuback( pxAzureIoTHubClient, pxPacketInfo, pxDeserializedInfo->usPacketIdentifier );
}
else if( ( azureiotmqttGET_PACKET_TYPE( pxPacketInfo->ucType ) ) == azureiotmqttPACKET_TYPE_PUBACK )
{
prvMQTTProcessPuback( pxAzureIoTHubClient, pxPacketInfo, pxDeserializedInfo->usPacketIdentifier );
}
else
{
AZLogDebug( ( "AzureIoTHubClient received packet of type: 0x%08x", pxPacketInfo->ucType ) );
}
}
/*-----------------------------------------------------------*/
/**
*
* Check/Process messages for incoming Cloud to Device messages.
*
* */
static uint32_t prvAzureIoTHubClientC2DProcess( AzureIoTHubClientReceiveContext_t * pxContext,
AzureIoTHubClient_t * pxAzureIoTHubClient,
void * pvPublishInfo )
{
AzureIoTResult_t xResult;
AzureIoTHubClientCloudToDeviceMessageRequest_t xCloudToDeviceMessage = { 0 };
AzureIoTMQTTPublishInfo_t * xMQTTPublishInfo = ( AzureIoTMQTTPublishInfo_t * ) pvPublishInfo;
az_result xCoreResult;
az_iot_hub_client_c2d_request xOutEmbeddedRequest;
az_span xTopicSpan = az_span_create( ( uint8_t * ) xMQTTPublishInfo->pcTopicName, xMQTTPublishInfo->usTopicNameLength );
/* Failed means no topic match. This means the message is not for cloud to device messaging. */
xCoreResult = az_iot_hub_client_c2d_parse_received_topic( &pxAzureIoTHubClient->_internal.xAzureIoTHubClientCore,
xTopicSpan, &xOutEmbeddedRequest );
if( az_result_failed( xCoreResult ) )
{
xResult = AzureIoT_TranslateCoreError( xCoreResult );
}
else
{
AZLogDebug( ( "Cloud xCloudToDeviceMessage topic: %.*s with payload : %.*s",
xMQTTPublishInfo->usTopicNameLength,
xMQTTPublishInfo->pcTopicName,
xMQTTPublishInfo->xPayloadLength,
( const char * ) xMQTTPublishInfo->pvPayload ) );
if( pxContext->_internal.callbacks.xCloudToDeviceMessageCallback )
{
xCloudToDeviceMessage.pvMessagePayload = xMQTTPublishInfo->pvPayload;
xCloudToDeviceMessage.ulPayloadLength = ( uint32_t ) xMQTTPublishInfo->xPayloadLength;
xCloudToDeviceMessage.xProperties._internal.xProperties = xOutEmbeddedRequest.properties;
AZLogDebug( ( "Invoking Cloud to Device callback" ) );
pxContext->_internal.callbacks.xCloudToDeviceMessageCallback( &xCloudToDeviceMessage,
pxContext->_internal.pvCallbackContext );
AZLogDebug( ( "Returned from Cloud to Device callback" ) );
}
xResult = eAzureIoTSuccess;
}
return ( uint32_t ) xResult;
}
/*-----------------------------------------------------------*/
/**
*
* Check/Process messages for incoming command messages.
*
* */
static uint32_t prvAzureIoTHubClientCommandProcess( AzureIoTHubClientReceiveContext_t * pxContext,
AzureIoTHubClient_t * pxAzureIoTHubClient,
void * pvPublishInfo )
{
AzureIoTResult_t xResult;
AzureIoTHubClientCommandRequest_t xCommandRequest = { 0 };
AzureIoTMQTTPublishInfo_t * xMQTTPublishInfo = ( AzureIoTMQTTPublishInfo_t * ) pvPublishInfo;
az_result xCoreResult;
az_iot_hub_client_command_request xOutEmbeddedRequest;
az_span xTopicSpan = az_span_create( ( uint8_t * ) xMQTTPublishInfo->pcTopicName, xMQTTPublishInfo->usTopicNameLength );
/* Failed means no topic match. This means the message is not for command. */
xCoreResult = az_iot_hub_client_commands_parse_received_topic( &pxAzureIoTHubClient->_internal.xAzureIoTHubClientCore,
xTopicSpan, &xOutEmbeddedRequest );
if( az_result_failed( xCoreResult ) )
{
xResult = AzureIoT_TranslateCoreError( xCoreResult );
}
else
{
AZLogDebug( ( "Command topic: %.*s with command payload : %.*s",
xMQTTPublishInfo->usTopicNameLength,
xMQTTPublishInfo->pcTopicName,
xMQTTPublishInfo->xPayloadLength,
( const char * ) xMQTTPublishInfo->pvPayload ) );
if( pxContext->_internal.callbacks.xCommandCallback )
{
xCommandRequest.pvMessagePayload = xMQTTPublishInfo->pvPayload;
xCommandRequest.ulPayloadLength = ( uint32_t ) xMQTTPublishInfo->xPayloadLength;
xCommandRequest.pucCommandName = az_span_ptr( xOutEmbeddedRequest.command_name );
xCommandRequest.usCommandNameLength = ( uint16_t ) az_span_size( xOutEmbeddedRequest.command_name );
xCommandRequest.pucComponentName = az_span_ptr( xOutEmbeddedRequest.component_name );
xCommandRequest.usComponentNameLength = ( uint16_t ) az_span_size( xOutEmbeddedRequest.component_name );
xCommandRequest.pucRequestID = az_span_ptr( xOutEmbeddedRequest.request_id );
xCommandRequest.usRequestIDLength = ( uint16_t ) az_span_size( xOutEmbeddedRequest.request_id );
AZLogDebug( ( "Invoking command callback" ) );
pxContext->_internal.callbacks.xCommandCallback( &xCommandRequest, pxContext->_internal.pvCallbackContext );
AZLogDebug( ( "Returned from command callback" ) );
}
xResult = eAzureIoTSuccess;
}
return ( uint32_t ) xResult;
}
/*-----------------------------------------------------------*/
/**
*
* Check/Process messages for incoming property messages.
*
* */
static uint32_t prvAzureIoTHubClientPropertiesProcess( AzureIoTHubClientReceiveContext_t * pxContext,
AzureIoTHubClient_t * pxAzureIoTHubClient,
void * pvPublishInfo )
{
AzureIoTResult_t xResult;
AzureIoTHubClientPropertiesResponse_t xPropertiesResponse = { 0 };
AzureIoTMQTTPublishInfo_t * xMQTTPublishInfo = ( AzureIoTMQTTPublishInfo_t * ) pvPublishInfo;
az_result xCoreResult;
az_iot_hub_client_properties_message xOutMessage;
az_span xTopicSpan = az_span_create( ( uint8_t * ) xMQTTPublishInfo->pcTopicName, xMQTTPublishInfo->usTopicNameLength );
uint32_t ulRequestID = 0;
/* Failed means no topic match. This means the message is not for properties messaging. */
xCoreResult = az_iot_hub_client_properties_parse_received_topic( &pxAzureIoTHubClient->_internal.xAzureIoTHubClientCore,
xTopicSpan, &xOutMessage );
if( az_result_failed( xCoreResult ) )
{
xResult = AzureIoT_TranslateCoreError( xCoreResult );
}
else
{
AZLogDebug( ( "Properties topic: %.*s. with payload : %.*s",
xMQTTPublishInfo->usTopicNameLength,
xMQTTPublishInfo->pcTopicName,
xMQTTPublishInfo->xPayloadLength,
( const char * ) xMQTTPublishInfo->pvPayload ) );
xResult = eAzureIoTSuccess;
if( pxContext->_internal.callbacks.xPropertiesCallback )
{
if( az_span_size( xOutMessage.request_id ) == 0 )
{
xPropertiesResponse.xMessageType = eAzureIoTHubPropertiesWritablePropertyMessage;
}
else
{
if( az_result_succeeded( xCoreResult = az_span_atou32( xOutMessage.request_id, &ulRequestID ) ) )
{
if( ulRequestID & 0x01 )
{
xPropertiesResponse.xMessageType = eAzureIoTHubPropertiesReportedResponseMessage;
}
else
{
xPropertiesResponse.xMessageType = eAzureIoTHubPropertiesRequestedMessage;
}
}
else
{
/* Failed to parse the message */
AZLogError( ( "Request ID parsing failed: core error=0x%08x", ( uint16_t ) xCoreResult ) );
xResult = AzureIoT_TranslateCoreError( xCoreResult );
}
}
if( xResult == eAzureIoTSuccess )
{
xPropertiesResponse.pvMessagePayload = xMQTTPublishInfo->pvPayload;
xPropertiesResponse.ulPayloadLength = ( uint32_t ) xMQTTPublishInfo->xPayloadLength;
xPropertiesResponse.xMessageStatus = ( AzureIoTHubMessageStatus_t ) xOutMessage.status;
xPropertiesResponse.ulRequestID = ulRequestID;
AZLogDebug( ( "Invoking property callback" ) );
pxContext->_internal.callbacks.xPropertiesCallback( &xPropertiesResponse,
pxContext->_internal.pvCallbackContext );
AZLogDebug( ( "Returning from property callback" ) );
}
}
}
return ( uint32_t ) xResult;
}
/*-----------------------------------------------------------*/
/**
*
* Time callback for MQTT initialization.
*
* */
static uint32_t prvGetTimeMs( void )
{
TickType_t xTickCount;
uint32_t ulTimeMs;
/* Get the current tick count. */
xTickCount = xTaskGetTickCount();
/* Convert the ticks to milliseconds. */
ulTimeMs = ( uint32_t ) xTickCount * azureiotMILLISECONDS_PER_TICK;
return ulTimeMs;
}
/*-----------------------------------------------------------*/
/**
* Get the next request Id available. Currently we are using
* odd for PropertiesReported property and even for PropertiesGet.
*
* */
static AzureIoTResult_t prvGetPropertiesRequestId( AzureIoTHubClient_t * pxAzureIoTHubClient,
az_span xSpan,
bool xOddSeq,
uint32_t * pulRequestId,
az_span * pxSpan )
{
AzureIoTResult_t xResult;
az_span xRemainder;
az_result xCoreResult;
/* Check the last request Id used, and do increment of 2 or 1 based on xOddSeq */
if( xOddSeq == ( bool ) ( pxAzureIoTHubClient->_internal.ulCurrentPropertyRequestID & 0x01 ) )
{
pxAzureIoTHubClient->_internal.ulCurrentPropertyRequestID += 2;
}
else
{
pxAzureIoTHubClient->_internal.ulCurrentPropertyRequestID += 1;
}
if( pxAzureIoTHubClient->_internal.ulCurrentPropertyRequestID == 0 )
{
pxAzureIoTHubClient->_internal.ulCurrentPropertyRequestID = 2;
}
xCoreResult = az_span_u32toa( xSpan, pxAzureIoTHubClient->_internal.ulCurrentPropertyRequestID, &xRemainder );
if( az_result_failed( xCoreResult ) )
{
AZLogError( ( "Couldn't convert request id to span" ) );
xResult = AzureIoT_TranslateCoreError( xCoreResult );
}
else
{
*pxSpan = az_span_slice( *pxSpan, 0, az_span_size( *pxSpan ) - az_span_size( xRemainder ) );
if( pulRequestId )
{
*pulRequestId = pxAzureIoTHubClient->_internal.ulCurrentPropertyRequestID;
}
xResult = eAzureIoTSuccess;
}
return xResult;
}
/*-----------------------------------------------------------*/
/**
* Do blocking wait for sub-ack of particular receive context.
*
**/
static AzureIoTResult_t prvWaitForSubAck( AzureIoTHubClient_t * pxAzureIoTHubClient,
AzureIoTHubClientReceiveContext_t * pxContext,
uint32_t ulTimeoutMilliseconds )
{
AzureIoTResult_t xResult = eAzureIoTErrorSubackWaitTimeout;
uint32_t ulWaitTime;
AZLogDebug( ( "Waiting for sub ack id: %d", pxContext->_internal.usMqttSubPacketID ) );
do
{
if( pxContext->_internal.usState == azureiothubTOPIC_SUBSCRIBE_STATE_SUBACK )
{
xResult = eAzureIoTSuccess;
break;
}
if( ulTimeoutMilliseconds > azureiothubSUBACK_WAIT_INTERVAL_MS )
{
ulTimeoutMilliseconds -= azureiothubSUBACK_WAIT_INTERVAL_MS;
ulWaitTime = azureiothubSUBACK_WAIT_INTERVAL_MS;
}
else
{
ulWaitTime = ulTimeoutMilliseconds;
ulTimeoutMilliseconds = 0;
}
if( AzureIoTMQTT_ProcessLoop( &pxAzureIoTHubClient->_internal.xMQTTContext, ulWaitTime ) != eAzureIoTMQTTSuccess )
{
xResult = eAzureIoTErrorFailed;
break;
}
} while( ulTimeoutMilliseconds );
if( pxContext->_internal.usState == azureiothubTOPIC_SUBSCRIBE_STATE_SUBACK )
{
xResult = eAzureIoTSuccess;
}
AZLogDebug( ( "Done waiting for sub ack id: %d, result: 0x%08x",
pxContext->_internal.usMqttSubPacketID, xResult ) );
return xResult;
}
/*-----------------------------------------------------------*/
/**
* Generate the SAS token based on :
* https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-security#use-a-shared-access-policy
**/
static uint32_t prvIoTHubClientGetToken( AzureIoTHubClient_t * pxAzureIoTHubClient,
uint64_t ullExpiryTimeSecs,
const uint8_t * ucKey,
uint32_t ulKeyLen,
uint8_t * pucSASBuffer,
uint32_t ulSasBufferLen,
uint32_t * pulSaSLength )
{
uint8_t * pucHMACBuffer;
az_span xSpan = az_span_create( pucSASBuffer, ( int32_t ) ulSasBufferLen );
az_result xCoreResult;
uint32_t ulSignatureLength;
uint32_t ulBytesUsed;
uint32_t ulBufferLeft;
size_t xLength;
xCoreResult = az_iot_hub_client_sas_get_signature( &( pxAzureIoTHubClient->_internal.xAzureIoTHubClientCore ),
ullExpiryTimeSecs, xSpan, &xSpan );
if( az_result_failed( xCoreResult ) )
{
AZLogError( ( "AzureIoTHubClient failed to get signature: core error=0x%08x", ( uint16_t ) xCoreResult ) );
return AzureIoT_TranslateCoreError( xCoreResult );
}
ulBytesUsed = ( uint32_t ) az_span_size( xSpan );
ulBufferLeft = ulSasBufferLen - ulBytesUsed;
if( ulBufferLeft < azureiothubHMACBufferLength )
{
AZLogError( ( "AzureIoTHubClient token generation failed with insufficient buffer size" ) );
return eAzureIoTErrorOutOfMemory;
}
/* Calculate HMAC at the end of buffer, so we do less data movement when returning back to caller. */
ulBufferLeft -= azureiothubHMACBufferLength;
pucHMACBuffer = pucSASBuffer + ulSasBufferLen - azureiothubHMACBufferLength;
if( AzureIoT_Base64HMACCalculate( pxAzureIoTHubClient->_internal.xHMACFunction,
ucKey, ulKeyLen, pucSASBuffer, ulBytesUsed,
pucSASBuffer + ulBytesUsed, ulBufferLeft,
pucHMACBuffer, azureiothubHMACBufferLength,
&ulSignatureLength ) != eAzureIoTSuccess )
{
AZLogError( ( "AzureIoTHubClient failed to encode HMAC hash" ) );
return eAzureIoTErrorFailed;
}
if( ( ulSasBufferLen <= azureiothubHMACBufferLength ) )
{
AZLogError( ( "AzureIoTHubClient failed with insufficient buffer size" ) );
return eAzureIoTErrorOutOfMemory;
}
xSpan = az_span_create( pucHMACBuffer, ( int32_t ) ulSignatureLength );
xCoreResult = az_iot_hub_client_sas_get_password( &( pxAzureIoTHubClient->_internal.xAzureIoTHubClientCore ),
ullExpiryTimeSecs, xSpan, AZ_SPAN_EMPTY, ( char * ) pucSASBuffer,
ulSasBufferLen - azureiothubHMACBufferLength,
&xLength );
if( az_result_failed( xCoreResult ) )
{
AZLogError( ( "AzureIoTHubClient failed to generate token: core error=0x%08x", ( uint16_t ) xCoreResult ) );
return AzureIoT_TranslateCoreError( xCoreResult );
}
*pulSaSLength = ( uint32_t ) xLength;
return eAzureIoTSuccess;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_OptionsInit( AzureIoTHubClientOptions_t * pxHubClientOptions )
{
AzureIoTResult_t xResult;
if( pxHubClientOptions == NULL )
{
AZLogError( ( "AzureIoTHubClient_OptionsInit failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else
{
memset( pxHubClientOptions, 0, sizeof( AzureIoTHubClientOptions_t ) );
pxHubClientOptions->pucUserAgent = ( const uint8_t * ) azureiothubUSER_AGENT;
pxHubClientOptions->ulUserAgentLength = sizeof( azureiothubUSER_AGENT ) - 1;
xResult = eAzureIoTSuccess;
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_Init( AzureIoTHubClient_t * pxAzureIoTHubClient,
const uint8_t * pucHostname,
uint16_t ulHostnameLength,
const uint8_t * pucDeviceId,
uint16_t ulDeviceIdLength,
AzureIoTHubClientOptions_t * pxHubClientOptions,
uint8_t * pucBuffer,
uint32_t ulBufferLength,
AzureIoTGetCurrentTimeFunc_t xGetTimeFunction,
const AzureIoTTransportInterface_t * pxTransportInterface )
{
AzureIoTResult_t xResult;
AzureIoTMQTTResult_t xMQTTResult;
az_result xCoreResult;
az_iot_hub_client_options xHubOptions;
uint8_t * pucNetworkBuffer;
uint32_t ulNetworkBufferLength;
az_span xHostnameSpan;
az_span xDeviceIDSpan;
if( ( pxAzureIoTHubClient == NULL ) ||
( pucHostname == NULL ) || ( ulHostnameLength == 0 ) ||
( pucDeviceId == NULL ) || ( ulDeviceIdLength == 0 ) ||
( pucBuffer == NULL ) || ( ulBufferLength == 0 ) ||
( xGetTimeFunction == NULL ) || ( pxTransportInterface == NULL ) )
{
AZLogError( ( "AzureIoTHubClient_Init failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else if( ( ulBufferLength < azureiotconfigTOPIC_MAX ) ||
( ulBufferLength < ( azureiotconfigUSERNAME_MAX + azureiotconfigPASSWORD_MAX ) ) )
{
AZLogError( ( "AzureIoTHubClient_Init failed: not enough memory passed" ) );
xResult = eAzureIoTErrorOutOfMemory;
}
else
{
memset( ( void * ) pxAzureIoTHubClient, 0, sizeof( AzureIoTHubClient_t ) );
/* Setup working buffer to be used by middleware */
pxAzureIoTHubClient->_internal.ulWorkingBufferLength =
( azureiotconfigUSERNAME_MAX + azureiotconfigPASSWORD_MAX ) > azureiotconfigTOPIC_MAX ?
( azureiotconfigUSERNAME_MAX + azureiotconfigPASSWORD_MAX ) : azureiotconfigTOPIC_MAX;
pxAzureIoTHubClient->_internal.pucWorkingBuffer = pucBuffer;
pucNetworkBuffer = pucBuffer + pxAzureIoTHubClient->_internal.ulWorkingBufferLength;
ulNetworkBufferLength = ulBufferLength - pxAzureIoTHubClient->_internal.ulWorkingBufferLength;
/* Initialize Azure IoT Hub Client */
xHostnameSpan = az_span_create( ( uint8_t * ) pucHostname, ( int32_t ) ulHostnameLength );
xDeviceIDSpan = az_span_create( ( uint8_t * ) pucDeviceId, ( int32_t ) ulDeviceIdLength );
xHubOptions = az_iot_hub_client_options_default();
if( pxHubClientOptions )
{
xHubOptions.module_id = az_span_create( ( uint8_t * ) pxHubClientOptions->pucModuleID, ( int32_t ) pxHubClientOptions->ulModuleIDLength );
xHubOptions.model_id = az_span_create( ( uint8_t * ) pxHubClientOptions->pucModelID, ( int32_t ) pxHubClientOptions->ulModelIDLength );
xHubOptions.user_agent = az_span_create( ( uint8_t * ) pxHubClientOptions->pucUserAgent, ( int32_t ) pxHubClientOptions->ulUserAgentLength );
xHubOptions.component_names = ( az_span * ) pxHubClientOptions->pxComponentList;
xHubOptions.component_names_length = ( int32_t ) pxHubClientOptions->ulComponentListLength;
}
else
{
xHubOptions.user_agent = az_span_create( ( uint8_t * ) azureiothubUSER_AGENT, sizeof( azureiothubUSER_AGENT ) - 1 );
}
if( az_result_failed( xCoreResult = az_iot_hub_client_init( &pxAzureIoTHubClient->_internal.xAzureIoTHubClientCore,
xHostnameSpan, xDeviceIDSpan, &xHubOptions ) ) )
{
AZLogError( ( "Failed to initialize az_iot_hub_client_init: core error=0x%08x", ( uint16_t ) xCoreResult ) );
xResult = AzureIoT_TranslateCoreError( xCoreResult );
}
/* Initialize AzureIoTMQTT library. */
else if( ( xMQTTResult = AzureIoTMQTT_Init( &( pxAzureIoTHubClient->_internal.xMQTTContext ), pxTransportInterface,
prvGetTimeMs, prvEventCallback,
pucNetworkBuffer, ulNetworkBufferLength ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Failed to initialize AzureIoTMQTT_Init: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorInitFailed;
}
else
{
pxAzureIoTHubClient->_internal.pucDeviceID = pucDeviceId;
pxAzureIoTHubClient->_internal.ulDeviceIDLength = ulDeviceIdLength;
pxAzureIoTHubClient->_internal.pucHostname = pucHostname;
pxAzureIoTHubClient->_internal.ulHostnameLength = ulHostnameLength;
pxAzureIoTHubClient->_internal.xTimeFunction = xGetTimeFunction;
pxAzureIoTHubClient->_internal.xTelemetryCallback =
pxHubClientOptions == NULL ? NULL : pxHubClientOptions->xTelemetryCallback;
xResult = eAzureIoTSuccess;
}
}
return xResult;
}
/*-----------------------------------------------------------*/
void AzureIoTHubClient_Deinit( AzureIoTHubClient_t * pxAzureIoTHubClient )
{
( void ) pxAzureIoTHubClient;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_SetSymmetricKey( AzureIoTHubClient_t * pxAzureIoTHubClient,
const uint8_t * pucSymmetricKey,
uint32_t ulSymmetricKeyLength,
AzureIoTGetHMACFunc_t xHMACFunction )
{
AzureIoTResult_t xResult;
if( ( pxAzureIoTHubClient == NULL ) ||
( pucSymmetricKey == NULL ) || ( ulSymmetricKeyLength == 0 ) ||
( xHMACFunction == NULL ) )
{
AZLogError( ( "AzureIoTHubClient_SetSymmetricKey failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else
{
pxAzureIoTHubClient->_internal.pucSymmetricKey = pucSymmetricKey;
pxAzureIoTHubClient->_internal.ulSymmetricKeyLength = ulSymmetricKeyLength;
pxAzureIoTHubClient->_internal.pxTokenRefresh = prvIoTHubClientGetToken;
pxAzureIoTHubClient->_internal.xHMACFunction = xHMACFunction;
xResult = eAzureIoTSuccess;
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_Connect( AzureIoTHubClient_t * pxAzureIoTHubClient,
bool xCleanSession,
bool * pxOutSessionPresent,
uint32_t ulTimeoutMilliseconds )
{
AzureIoTMQTTConnectInfo_t xConnectInfo = { 0 };
AzureIoTResult_t xResult;
AzureIoTMQTTResult_t xMQTTResult;
uint32_t ulPasswordLength = 0;
size_t xMQTTUserNameLength;
az_result xCoreResult;
if( ( pxAzureIoTHubClient == NULL ) || ( pxOutSessionPresent == NULL ) )
{
AZLogError( ( "AzureIoTHubClient_Connect failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else
{
/* Use working buffer for username/password */
xConnectInfo.pcUserName = pxAzureIoTHubClient->_internal.pucWorkingBuffer;
xConnectInfo.pcPassword = xConnectInfo.pcUserName + azureiotconfigUSERNAME_MAX;
if( az_result_failed( xCoreResult = az_iot_hub_client_get_user_name( &pxAzureIoTHubClient->_internal.xAzureIoTHubClientCore,
( char * ) xConnectInfo.pcUserName, azureiotconfigUSERNAME_MAX,
&xMQTTUserNameLength ) ) )
{
AZLogError( ( "Failed to get username: core error=0x%08x", ( uint16_t ) xCoreResult ) );
xResult = AzureIoT_TranslateCoreError( xCoreResult );
}
/* Check if token refresh is set, then generate password */
else if( ( pxAzureIoTHubClient->_internal.pxTokenRefresh ) &&
( pxAzureIoTHubClient->_internal.pxTokenRefresh( pxAzureIoTHubClient,
pxAzureIoTHubClient->_internal.xTimeFunction() +
azureiothubDEFAULT_TOKEN_TIMEOUT_IN_SEC,
pxAzureIoTHubClient->_internal.pucSymmetricKey,
pxAzureIoTHubClient->_internal.ulSymmetricKeyLength,
( uint8_t * ) xConnectInfo.pcPassword, azureiotconfigPASSWORD_MAX,
&ulPasswordLength ) ) )
{
AZLogError( ( "Failed to generate SAS token" ) );
xResult = eAzureIoTErrorFailed;
}
else
{
xConnectInfo.xCleanSession = xCleanSession;
xConnectInfo.pcClientIdentifier = pxAzureIoTHubClient->_internal.pucDeviceID;
xConnectInfo.usClientIdentifierLength = ( uint16_t ) pxAzureIoTHubClient->_internal.ulDeviceIDLength;
xConnectInfo.usUserNameLength = ( uint16_t ) xMQTTUserNameLength;
xConnectInfo.usKeepAliveSeconds = azureiothubKEEP_ALIVE_TIMEOUT_SECONDS;
xConnectInfo.usPasswordLength = ( uint16_t ) ulPasswordLength;
/* Send MQTT CONNECT packet to broker. Last Will and Testament is not used. */
if( ( xMQTTResult = AzureIoTMQTT_Connect( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
&xConnectInfo,
NULL,
ulTimeoutMilliseconds,
pxOutSessionPresent ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Failed to establish MQTT connection: Server=%.*s, MQTT error=0x%08x",
pxAzureIoTHubClient->_internal.ulHostnameLength, ( const char * ) pxAzureIoTHubClient->_internal.pucHostname,
xMQTTResult ) );
xResult = eAzureIoTErrorFailed;
}
else
{
/* Successfully established a MQTT connection with the broker. */
AZLogInfo( ( "An MQTT connection is established with %.*s", pxAzureIoTHubClient->_internal.ulHostnameLength,
( const char * ) pxAzureIoTHubClient->_internal.pucHostname ) );
xResult = eAzureIoTSuccess;
}
}
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_Disconnect( AzureIoTHubClient_t * pxAzureIoTHubClient )
{
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
if( pxAzureIoTHubClient == NULL )
{
AZLogError( ( "AzureIoTHubClient_Disconnect failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else if( ( xMQTTResult = AzureIoTMQTT_Disconnect( &( pxAzureIoTHubClient->_internal.xMQTTContext ) ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "AzureIoTHubClient_Disconnect failed to disconnect: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorFailed;
}
else
{
AZLogInfo( ( "Disconnecting the MQTT connection with %.*s", pxAzureIoTHubClient->_internal.ulHostnameLength,
( const char * ) pxAzureIoTHubClient->_internal.pucHostname ) );
xResult = eAzureIoTSuccess;
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_SendTelemetry( AzureIoTHubClient_t * pxAzureIoTHubClient,
const uint8_t * pucTelemetryData,
uint32_t ulTelemetryDataLength,
AzureIoTMessageProperties_t * pxProperties,
AzureIoTHubMessageQoS_t xQOS,
uint16_t * pusTelemetryPacketID )
{
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
AzureIoTMQTTPublishInfo_t xMQTTPublishInfo = { 0 };
uint16_t usPublishPacketIdentifier = 0;
size_t xTelemetryTopicLength;
az_result xCoreResult;
if( pxAzureIoTHubClient == NULL )
{
AZLogError( ( "AzureIoTHubClient_SendTelemetry failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else if( az_result_failed(
xCoreResult = az_iot_hub_client_telemetry_get_publish_topic( &pxAzureIoTHubClient->_internal.xAzureIoTHubClientCore,
( pxProperties != NULL ) ? &pxProperties->_internal.xProperties : NULL,
( char * ) pxAzureIoTHubClient->_internal.pucWorkingBuffer,
pxAzureIoTHubClient->_internal.ulWorkingBufferLength,
&xTelemetryTopicLength ) ) )
{
AZLogError( ( "Failed to get telemetry topic: core error=0x%08x", ( uint16_t ) xCoreResult ) );
xResult = AzureIoT_TranslateCoreError( xCoreResult );
}
else
{
xMQTTPublishInfo.xQOS = xQOS == eAzureIoTHubMessageQoS1 ? eAzureIoTMQTTQoS1 : eAzureIoTMQTTQoS0;
xMQTTPublishInfo.pcTopicName = pxAzureIoTHubClient->_internal.pucWorkingBuffer;
xMQTTPublishInfo.usTopicNameLength = ( uint16_t ) xTelemetryTopicLength;
xMQTTPublishInfo.pvPayload = ( const void * ) pucTelemetryData;
xMQTTPublishInfo.xPayloadLength = ulTelemetryDataLength;
/* Get a unique packet id. Not used if QOS is 0 */
if( xQOS == eAzureIoTHubMessageQoS1 )
{
usPublishPacketIdentifier = AzureIoTMQTT_GetPacketId( &( pxAzureIoTHubClient->_internal.xMQTTContext ) );
}
/* Send PUBLISH packet. */
if( ( xMQTTResult = AzureIoTMQTT_Publish( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
&xMQTTPublishInfo, usPublishPacketIdentifier ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Failed to publish telemetry: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorPublishFailed;
}
else
{
if( ( xQOS == eAzureIoTHubMessageQoS1 ) && ( pusTelemetryPacketID != NULL ) )
{
*pusTelemetryPacketID = usPublishPacketIdentifier;
}
AZLogInfo( ( "Successfully sent telemetry message" ) );
xResult = eAzureIoTSuccess;
}
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_ProcessLoop( AzureIoTHubClient_t * pxAzureIoTHubClient,
uint32_t ulTimeoutMilliseconds )
{
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
if( pxAzureIoTHubClient == NULL )
{
AZLogError( ( "AzureIoTHubClient_ProcessLoop failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else if( ( xMQTTResult = AzureIoTMQTT_ProcessLoop( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
ulTimeoutMilliseconds ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "AzureIoTMQTT_ProcessLoop failed: ProcessLoopDuration=%u, MQTT error=0x%08x",
( uint16_t ) ulTimeoutMilliseconds, ( uint16_t ) xMQTTResult ) );
xResult = eAzureIoTErrorFailed;
}
else
{
xResult = eAzureIoTSuccess;
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_SubscribeCloudToDeviceMessage( AzureIoTHubClient_t * pxAzureIoTHubClient,
AzureIoTHubClientCloudToDeviceMessageCallback_t xCallback,
void * prvCallbackContext,
uint32_t ulTimeoutMilliseconds )
{
AzureIoTMQTTSubscribeInfo_t xMqttSubscription = { 0 };
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
uint16_t usSubscribePacketIdentifier;
AzureIoTHubClientReceiveContext_t * pxContext;
if( ( pxAzureIoTHubClient == NULL ) ||
( xCallback == NULL ) )
{
AZLogError( ( "AzureIoTHubClient_SubscribeCloudToDeviceMessage failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else
{
pxContext = &pxAzureIoTHubClient->_internal.xReceiveContext[ azureiothubRECEIVE_CONTEXT_INDEX_C2D ];
xMqttSubscription.xQoS = eAzureIoTMQTTQoS1;
xMqttSubscription.pcTopicFilter = ( const uint8_t * ) AZ_IOT_HUB_CLIENT_C2D_SUBSCRIBE_TOPIC;
xMqttSubscription.usTopicFilterLength = ( uint16_t ) sizeof( AZ_IOT_HUB_CLIENT_C2D_SUBSCRIBE_TOPIC ) - 1;
usSubscribePacketIdentifier = AzureIoTMQTT_GetPacketId( &( pxAzureIoTHubClient->_internal.xMQTTContext ) );
AZLogDebug( ( "Attempting to subscribe to the MQTT topic: %s", AZ_IOT_HUB_CLIENT_C2D_SUBSCRIBE_TOPIC ) );
if( ( xMQTTResult = AzureIoTMQTT_Subscribe( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
&xMqttSubscription, 1, usSubscribePacketIdentifier ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Cloud to device subscribe failed: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorSubscribeFailed;
}
else
{
pxContext->_internal.usState = azureiothubTOPIC_SUBSCRIBE_STATE_SUB;
pxContext->_internal.usMqttSubPacketID = usSubscribePacketIdentifier;
pxContext->_internal.pxProcessFunction = prvAzureIoTHubClientC2DProcess;
pxContext->_internal.callbacks.xCloudToDeviceMessageCallback = xCallback;
pxContext->_internal.pvCallbackContext = prvCallbackContext;
if( ( xResult = prvWaitForSubAck( pxAzureIoTHubClient, pxContext,
ulTimeoutMilliseconds ) ) != eAzureIoTSuccess )
{
AZLogError( ( "Wait for cloud to device sub ack failed : error=0x%08x", xResult ) );
memset( pxContext, 0, sizeof( AzureIoTHubClientReceiveContext_t ) );
}
}
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_UnsubscribeCloudToDeviceMessage( AzureIoTHubClient_t * pxAzureIoTHubClient )
{
AzureIoTMQTTSubscribeInfo_t xMqttSubscription = { 0 };
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
uint16_t usSubscribePacketIdentifier;
AzureIoTHubClientReceiveContext_t * pxContext;
if( pxAzureIoTHubClient == NULL )
{
AZLogError( ( "AzureIoTHubClient_UnsubscribeCloudToDeviceMessage failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else
{
pxContext = &pxAzureIoTHubClient->_internal.xReceiveContext[ azureiothubRECEIVE_CONTEXT_INDEX_C2D ];
xMqttSubscription.xQoS = eAzureIoTMQTTQoS1;
xMqttSubscription.pcTopicFilter = ( const uint8_t * ) AZ_IOT_HUB_CLIENT_C2D_SUBSCRIBE_TOPIC;
xMqttSubscription.usTopicFilterLength = ( uint16_t ) sizeof( AZ_IOT_HUB_CLIENT_C2D_SUBSCRIBE_TOPIC ) - 1;
usSubscribePacketIdentifier = AzureIoTMQTT_GetPacketId( &( pxAzureIoTHubClient->_internal.xMQTTContext ) );
AZLogDebug( ( "Attempting to unsubscribe from the MQTT topic: %s", AZ_IOT_HUB_CLIENT_C2D_SUBSCRIBE_TOPIC ) );
if( ( xMQTTResult = AzureIoTMQTT_Unsubscribe( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
&xMqttSubscription, 1,
usSubscribePacketIdentifier ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Cloud to device unsubscribe failed: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorUnsubscribeFailed;
}
else
{
memset( pxContext, 0, sizeof( AzureIoTHubClientReceiveContext_t ) );
xResult = eAzureIoTSuccess;
}
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_SubscribeCommand( AzureIoTHubClient_t * pxAzureIoTHubClient,
AzureIoTHubClientCommandCallback_t xCallback,
void * prvCallbackContext,
uint32_t ulTimeoutMilliseconds )
{
AzureIoTMQTTSubscribeInfo_t xMqttSubscription = { 0 };
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
uint16_t usSubscribePacketIdentifier;
AzureIoTHubClientReceiveContext_t * pxContext;
if( ( pxAzureIoTHubClient == NULL ) ||
( xCallback == NULL ) )
{
AZLogError( ( "AzureIoTHubClient_SubscribeCommand failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else
{
pxContext = &pxAzureIoTHubClient->_internal.xReceiveContext[ azureiothubRECEIVE_CONTEXT_INDEX_COMMANDS ];
xMqttSubscription.xQoS = eAzureIoTMQTTQoS0;
xMqttSubscription.pcTopicFilter = ( const uint8_t * ) AZ_IOT_HUB_CLIENT_COMMANDS_SUBSCRIBE_TOPIC;
xMqttSubscription.usTopicFilterLength = ( uint16_t ) sizeof( AZ_IOT_HUB_CLIENT_COMMANDS_SUBSCRIBE_TOPIC ) - 1;
usSubscribePacketIdentifier = AzureIoTMQTT_GetPacketId( &( pxAzureIoTHubClient->_internal.xMQTTContext ) );
AZLogDebug( ( "Attempting to subscribe to the MQTT topic: %s", AZ_IOT_HUB_CLIENT_COMMANDS_SUBSCRIBE_TOPIC ) );
if( ( xMQTTResult = AzureIoTMQTT_Subscribe( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
&xMqttSubscription, 1,
usSubscribePacketIdentifier ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Command subscribe failed: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorSubscribeFailed;
}
else
{
pxContext->_internal.usState = azureiothubTOPIC_SUBSCRIBE_STATE_SUB;
pxContext->_internal.usMqttSubPacketID = usSubscribePacketIdentifier;
pxContext->_internal.pxProcessFunction = prvAzureIoTHubClientCommandProcess;
pxContext->_internal.callbacks.xCommandCallback = xCallback;
pxContext->_internal.pvCallbackContext = prvCallbackContext;
if( ( xResult = prvWaitForSubAck( pxAzureIoTHubClient, pxContext,
ulTimeoutMilliseconds ) ) != eAzureIoTSuccess )
{
AZLogError( ( "Wait for command sub ack failed: error=0x%08x", xResult ) );
memset( pxContext, 0, sizeof( AzureIoTHubClientReceiveContext_t ) );
}
}
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_UnsubscribeCommand( AzureIoTHubClient_t * pxAzureIoTHubClient )
{
AzureIoTMQTTSubscribeInfo_t xMqttSubscription = { 0 };
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
uint16_t usSubscribePacketIdentifier;
AzureIoTHubClientReceiveContext_t * pxContext;
if( pxAzureIoTHubClient == NULL )
{
AZLogError( ( "AzureIoTHubClient_UnsubscribeCommand failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else
{
pxContext = &pxAzureIoTHubClient->_internal.xReceiveContext[ azureiothubRECEIVE_CONTEXT_INDEX_COMMANDS ];
xMqttSubscription.xQoS = eAzureIoTMQTTQoS0;
xMqttSubscription.pcTopicFilter = ( const uint8_t * ) AZ_IOT_HUB_CLIENT_COMMANDS_SUBSCRIBE_TOPIC;
xMqttSubscription.usTopicFilterLength = ( uint16_t ) sizeof( AZ_IOT_HUB_CLIENT_COMMANDS_SUBSCRIBE_TOPIC ) - 1;
usSubscribePacketIdentifier = AzureIoTMQTT_GetPacketId( &( pxAzureIoTHubClient->_internal.xMQTTContext ) );
AZLogDebug( ( "Attempting to unsubscribe from the MQTT topic: %s", AZ_IOT_HUB_CLIENT_COMMANDS_SUBSCRIBE_TOPIC ) );
if( ( xMQTTResult = AzureIoTMQTT_Unsubscribe( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
&xMqttSubscription, 1,
usSubscribePacketIdentifier ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Command unsubscribe failed: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorUnsubscribeFailed;
}
else
{
memset( pxContext, 0, sizeof( AzureIoTHubClientReceiveContext_t ) );
xResult = eAzureIoTSuccess;
}
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_SendCommandResponse( AzureIoTHubClient_t * pxAzureIoTHubClient,
const AzureIoTHubClientCommandRequest_t * pxMessage,
uint32_t ulStatus,
const uint8_t * pucCommandPayload,
uint32_t ulCommandPayloadLength )
{
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
AzureIoTMQTTPublishInfo_t xMQTTPublishInfo = { 0 };
az_span xRequestID;
size_t xTopicLength;
az_result xCoreResult;
if( ( pxAzureIoTHubClient == NULL ) ||
( pxMessage == NULL ) )
{
AZLogError( ( "AzureIoTHubClient_SendCommandResponse failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else if( ( pxMessage->pucRequestID == NULL ) || ( pxMessage->usRequestIDLength == 0 ) )
{
AZLogError( ( "AzureIoTHubClient_SendCommandResponse failed: invalid request id " ) );
xResult = eAzureIoTErrorFailed;
}
else
{
xRequestID = az_span_create( ( uint8_t * ) pxMessage->pucRequestID, ( int32_t ) pxMessage->usRequestIDLength );
if( az_result_failed(
xCoreResult =
az_iot_hub_client_commands_response_get_publish_topic( &pxAzureIoTHubClient->_internal.xAzureIoTHubClientCore,
xRequestID, ( uint16_t ) ulStatus,
( char * ) pxAzureIoTHubClient->_internal.pucWorkingBuffer,
pxAzureIoTHubClient->_internal.ulWorkingBufferLength,
&xTopicLength ) ) )
{
AZLogError( ( "Failed to get command response topic: core error=0x%08x", ( uint16_t ) xCoreResult ) );
xResult = AzureIoT_TranslateCoreError( xCoreResult );
}
else
{
xMQTTPublishInfo.xQOS = eAzureIoTMQTTQoS0;
xMQTTPublishInfo.pcTopicName = pxAzureIoTHubClient->_internal.pucWorkingBuffer;
xMQTTPublishInfo.usTopicNameLength = ( uint16_t ) xTopicLength;
if( ( pucCommandPayload == NULL ) || ( ulCommandPayloadLength == 0 ) )
{
xMQTTPublishInfo.pvPayload = ( const void * ) azureiothubCOMMAND_EMPTY_RESPONSE;
xMQTTPublishInfo.xPayloadLength = sizeof( azureiothubCOMMAND_EMPTY_RESPONSE ) - 1;
}
else
{
xMQTTPublishInfo.pvPayload = ( const void * ) pucCommandPayload;
xMQTTPublishInfo.xPayloadLength = ulCommandPayloadLength;
}
if( ( xMQTTResult = AzureIoTMQTT_Publish( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
&xMQTTPublishInfo, 0 ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Failed to publish response: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorPublishFailed;
}
else
{
xResult = eAzureIoTSuccess;
}
}
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_SubscribeProperties( AzureIoTHubClient_t * pxAzureIoTHubClient,
AzureIoTHubClientPropertiesCallback_t xCallback,
void * prvCallbackContext,
uint32_t ulTimeoutMilliseconds )
{
AzureIoTMQTTSubscribeInfo_t xMqttSubscription[ 2 ] = { { 0 }, { 0 } };
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
uint16_t usSubscribePacketIdentifier;
AzureIoTHubClientReceiveContext_t * pxContext;
if( ( pxAzureIoTHubClient == NULL ) ||
( xCallback == NULL ) )
{
AZLogError( ( "AzureIoTHubClient_SubscribeProperties failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else
{
pxContext = &pxAzureIoTHubClient->_internal.xReceiveContext[ azureiothubRECEIVE_CONTEXT_INDEX_PROPERTIES ];
xMqttSubscription[ 0 ].xQoS = eAzureIoTMQTTQoS0;
xMqttSubscription[ 0 ].pcTopicFilter = ( const uint8_t * ) AZ_IOT_HUB_CLIENT_PROPERTIES_MESSAGE_SUBSCRIBE_TOPIC;
xMqttSubscription[ 0 ].usTopicFilterLength = ( uint16_t ) sizeof( AZ_IOT_HUB_CLIENT_PROPERTIES_MESSAGE_SUBSCRIBE_TOPIC ) - 1;
xMqttSubscription[ 1 ].xQoS = eAzureIoTMQTTQoS0;
xMqttSubscription[ 1 ].pcTopicFilter = ( const uint8_t * ) AZ_IOT_HUB_CLIENT_PROPERTIES_WRITABLE_UPDATES_SUBSCRIBE_TOPIC;
xMqttSubscription[ 1 ].usTopicFilterLength = ( uint16_t ) sizeof( AZ_IOT_HUB_CLIENT_PROPERTIES_WRITABLE_UPDATES_SUBSCRIBE_TOPIC ) - 1;
usSubscribePacketIdentifier = AzureIoTMQTT_GetPacketId( &( pxAzureIoTHubClient->_internal.xMQTTContext ) );
AZLogDebug( ( "Attempting to subscribe to the MQTT topics: %s and %s",
AZ_IOT_HUB_CLIENT_PROPERTIES_MESSAGE_SUBSCRIBE_TOPIC, AZ_IOT_HUB_CLIENT_PROPERTIES_WRITABLE_UPDATES_SUBSCRIBE_TOPIC ) );
if( ( xMQTTResult = AzureIoTMQTT_Subscribe( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
xMqttSubscription, 2,
usSubscribePacketIdentifier ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Properties subscribe failed: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorSubscribeFailed;
}
else
{
pxContext->_internal.usState = azureiothubTOPIC_SUBSCRIBE_STATE_SUB;
pxContext->_internal.usMqttSubPacketID = usSubscribePacketIdentifier;
pxContext->_internal.pxProcessFunction = prvAzureIoTHubClientPropertiesProcess;
pxContext->_internal.callbacks.xPropertiesCallback = xCallback;
pxContext->_internal.pvCallbackContext = prvCallbackContext;
if( ( xResult = prvWaitForSubAck( pxAzureIoTHubClient, pxContext,
ulTimeoutMilliseconds ) ) != eAzureIoTSuccess )
{
AZLogError( ( "Wait for properties sub ack failed: error=0x%08x", xResult ) );
memset( pxContext, 0, sizeof( AzureIoTHubClientReceiveContext_t ) );
}
}
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_UnsubscribeProperties( AzureIoTHubClient_t * pxAzureIoTHubClient )
{
AzureIoTMQTTSubscribeInfo_t xMqttSubscription[ 2 ] = { { 0 }, { 0 } };
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
uint16_t usSubscribePacketIdentifier;
AzureIoTHubClientReceiveContext_t * pxContext;
if( pxAzureIoTHubClient == NULL )
{
AZLogError( ( "AzureIoTHubClient_UnsubscribeProperties failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else
{
pxContext = &pxAzureIoTHubClient->_internal.xReceiveContext[ azureiothubRECEIVE_CONTEXT_INDEX_PROPERTIES ];
xMqttSubscription[ 0 ].xQoS = eAzureIoTMQTTQoS0;
xMqttSubscription[ 0 ].pcTopicFilter = ( const uint8_t * ) AZ_IOT_HUB_CLIENT_PROPERTIES_MESSAGE_SUBSCRIBE_TOPIC;
xMqttSubscription[ 0 ].usTopicFilterLength = ( uint16_t ) sizeof( AZ_IOT_HUB_CLIENT_PROPERTIES_MESSAGE_SUBSCRIBE_TOPIC ) - 1;
xMqttSubscription[ 1 ].xQoS = eAzureIoTMQTTQoS0;
xMqttSubscription[ 1 ].pcTopicFilter = ( const uint8_t * ) AZ_IOT_HUB_CLIENT_PROPERTIES_WRITABLE_UPDATES_SUBSCRIBE_TOPIC;
xMqttSubscription[ 1 ].usTopicFilterLength = ( uint16_t ) sizeof( AZ_IOT_HUB_CLIENT_PROPERTIES_WRITABLE_UPDATES_SUBSCRIBE_TOPIC ) - 1;
usSubscribePacketIdentifier = AzureIoTMQTT_GetPacketId( &( pxAzureIoTHubClient->_internal.xMQTTContext ) );
AZLogDebug( ( "Attempting to unsubscribe from MQTT topics: %s and %s",
AZ_IOT_HUB_CLIENT_PROPERTIES_MESSAGE_SUBSCRIBE_TOPIC, AZ_IOT_HUB_CLIENT_PROPERTIES_WRITABLE_UPDATES_SUBSCRIBE_TOPIC ) );
if( ( xMQTTResult = AzureIoTMQTT_Unsubscribe( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
xMqttSubscription, 2,
usSubscribePacketIdentifier ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Failed to unsubscribe: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorUnsubscribeFailed;
}
else
{
memset( pxContext, 0, sizeof( AzureIoTHubClientReceiveContext_t ) );
xResult = eAzureIoTSuccess;
}
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_SendPropertiesReported( AzureIoTHubClient_t * pxAzureIoTHubClient,
const uint8_t * pucReportedPayload,
uint32_t ulReportedPayloadLength,
uint32_t * pulRequestId )
{
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
AzureIoTMQTTPublishInfo_t xMQTTPublishInfo = { 0 };
uint8_t ucRequestID[ azureiothubMAX_SIZE_FOR_UINT32 ];
size_t xTopicLength;
az_result xCoreResult;
az_span xRequestID = az_span_create( ucRequestID, sizeof( ucRequestID ) );
if( ( pxAzureIoTHubClient == NULL ) ||
( pucReportedPayload == NULL ) || ( ulReportedPayloadLength == 0 ) )
{
AZLogError( ( "AzureIoTHubClient_SendPropertiesReported failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else if( pxAzureIoTHubClient->_internal.xReceiveContext[ azureiothubRECEIVE_CONTEXT_INDEX_PROPERTIES ]._internal.usState !=
azureiothubTOPIC_SUBSCRIBE_STATE_SUBACK )
{
AZLogError( ( "AzureIoTHubClient_SendPropertiesReported failed: property topic not subscribed" ) );
xResult = eAzureIoTErrorTopicNotSubscribed;
}
else
{
if( ( xResult = prvGetPropertiesRequestId( pxAzureIoTHubClient, xRequestID,
true, pulRequestId, &xRequestID ) ) != eAzureIoTSuccess )
{
AZLogError( ( "Failed to get request id: error=0x%08x", xResult ) );
}
else if( az_result_failed(
xCoreResult =
az_iot_hub_client_properties_get_reported_publish_topic( &pxAzureIoTHubClient->_internal.xAzureIoTHubClientCore,
xRequestID,
( char * ) pxAzureIoTHubClient->_internal.pucWorkingBuffer,
pxAzureIoTHubClient->_internal.ulWorkingBufferLength,
&xTopicLength ) ) )
{
AZLogError( ( "Failed to get property patch topic: core error=0x%08x", ( uint16_t ) xCoreResult ) );
xResult = AzureIoT_TranslateCoreError( xCoreResult );
}
else
{
xMQTTPublishInfo.xQOS = eAzureIoTMQTTQoS0;
xMQTTPublishInfo.pcTopicName = pxAzureIoTHubClient->_internal.pucWorkingBuffer;
xMQTTPublishInfo.usTopicNameLength = ( uint16_t ) xTopicLength;
xMQTTPublishInfo.pvPayload = ( const void * ) pucReportedPayload;
xMQTTPublishInfo.xPayloadLength = ulReportedPayloadLength;
if( ( xMQTTResult = AzureIoTMQTT_Publish( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
&xMQTTPublishInfo, 0 ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Failed to Publish properties reported message: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorPublishFailed;
}
else
{
xResult = eAzureIoTSuccess;
}
}
}
return xResult;
}
/*-----------------------------------------------------------*/
AzureIoTResult_t AzureIoTHubClient_RequestPropertiesAsync( AzureIoTHubClient_t * pxAzureIoTHubClient )
{
AzureIoTMQTTResult_t xMQTTResult;
AzureIoTResult_t xResult;
AzureIoTMQTTPublishInfo_t xMQTTPublishInfo = { 0 };
uint8_t ucRequestID[ 10 ];
az_span xRequestID = az_span_create( ( uint8_t * ) ucRequestID, sizeof( ucRequestID ) );
size_t xTopicLength;
az_result xCoreResult;
if( pxAzureIoTHubClient == NULL )
{
AZLogError( ( "AzureIoTHubClient_RequestPropertiesAsync failed: invalid argument" ) );
xResult = eAzureIoTErrorInvalidArgument;
}
else if( pxAzureIoTHubClient->_internal.xReceiveContext[ azureiothubRECEIVE_CONTEXT_INDEX_PROPERTIES ]._internal.usState !=
azureiothubTOPIC_SUBSCRIBE_STATE_SUBACK )
{
AZLogError( ( "AzureIoTHubClient_RequestPropertiesAsync failed: properties topic not subscribed" ) );
xResult = eAzureIoTErrorTopicNotSubscribed;
}
else
{
if( ( xResult = prvGetPropertiesRequestId( pxAzureIoTHubClient, xRequestID,
false, NULL, &xRequestID ) ) != eAzureIoTSuccess )
{
AZLogError( ( "Failed to get request id: error=0x%08x", xResult ) );
}
else if( az_result_failed(
xCoreResult =
az_iot_hub_client_properties_document_get_publish_topic( &pxAzureIoTHubClient->_internal.xAzureIoTHubClientCore,
xRequestID,
( char * ) pxAzureIoTHubClient->_internal.pucWorkingBuffer,
pxAzureIoTHubClient->_internal.ulWorkingBufferLength,
&xTopicLength ) ) )
{
AZLogError( ( "Failed to get property document topic: core error=0x%08x", ( uint16_t ) xCoreResult ) );
xResult = AzureIoT_TranslateCoreError( xCoreResult );
}
else
{
xMQTTPublishInfo.pcTopicName = pxAzureIoTHubClient->_internal.pucWorkingBuffer;
xMQTTPublishInfo.xQOS = eAzureIoTMQTTQoS0;
xMQTTPublishInfo.usTopicNameLength = ( uint16_t ) xTopicLength;
if( ( xMQTTResult = AzureIoTMQTT_Publish( &( pxAzureIoTHubClient->_internal.xMQTTContext ),
&xMQTTPublishInfo, 0 ) ) != eAzureIoTMQTTSuccess )
{
AZLogError( ( "Failed to Publish get properties message: MQTT error=0x%08x", xMQTTResult ) );
xResult = eAzureIoTErrorPublishFailed;
}
else
{
xResult = eAzureIoTSuccess;
}
}
}
return xResult;
}
/*-----------------------------------------------------------*/