blob: 25b7d77a539d00732d4fcefb07fb77923a7f3700 [file] [log] [blame]
/*
* FreeRTOS Kernel V10.3.0
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/* A device's Shadow is a JSON document that is used to store and retrieve
* current state information for a device in AWS (Amazon Web Services) Cloud.
* Please refer to the AWS documentation for more details about Device Shadow
* service.
* https://docs.aws.amazon.com/iot/latest/developerguide/iot-device-shadows.html
*
* This demo creates a single application task that loops through a set of
* examples that demonstrates usage of Shadow library. Please find the list of
* Shadow operations and callbacks used in this demo below.
* Shadow Delete - Deletes the Shadow document of the device.
* Shadow Update - Updates the Shadow document with JSON document
* provided.
* Shadow Delta Callback - Callback to be invoked when Shadow document is
* updated with different desired and reported
* states.
* Shadow Updated Callback - Callback to be invoked when a Shadow document is
* updated.
*
* The demo program uses Shadow Update and Shadow Delta Callbacks to simulate
* toggling a remote device's state. It sends a Shadow Update with a new
* desired state and waits for the device to change its reported state in
* Shadow Delta Callback as a response to the new desired state. In addition,
* a Shadow Updated Callback is used to print the changing Shadow states.
* Shadow Updates are done #shadowexampleUPDATE_COUNT times in each loop.At the
* end of each loop Shadow document is deleted using Shadow Delete.
*
* In this demo both update to the desired state of Shadow document and
* response to it by updating the reported state is done in the same
* application. This is to illustrate the capability of the Shadow service and
* library in a single demo. Ideally the update to the desired state can be
* from an external application to control the state of the device. This demo
* is meant to demonstrate the usage of different Shadow library APIs.
*
* The Shadow demo uses a MQTT connection to connect to Shadow Service in AWS.
* Mutual authentication between AWS IoT MQTT Broker and Device is required to
* run this demo. Please complete configurations in aws_iot_demo_profile.h for
* mutual authentication.
* More details for mutual authentication configuration can be found at
* https://www.freertos.org/mqtt/preconfiguredexamplesMA.html
*
* Note: The parser used in this demo is specific for parsing AWS IoT document
* received through a mutually authenticated connection with AWS IoT MQTT
* broker. This parser will not check for the correctness of the document as it
* is designed for low memory footprint rather than checking for correctness of
* the JSON document. This parser is not meant to be used as a general purpose
* JSON parser.
*/
/* Standard includes. */
#include <string.h>
#include <stdio.h>
/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
/* IoT SDK includes. */
#include "aws_iot_shadow.h"
#include "aws_iot_demo_profile.h"
#include "iot_mqtt.h"
#include "iot_taskpool_freertos.h"
#include "aws_iot_doc_parser.h"
#include "platform/iot_clock.h"
#include "platform/iot_threads.h"
#include "platform/iot_network_freertos.h"
/* Preprocessor check iot configuration */
#include "aws_iot_setup_check.h"
/* Demo specific includes. */
#include "demo_config.h"
/**
* @brief The keep-alive interval used for this example.
*
* An MQTT ping request will be sent periodically at this interval.
*
* @note: This value is set to zero to disable MQTT
* keep alive for the Windows simulator project.
* The FreeRTOS kernel does not accurately calculate time for the Windows
* Simulator. Therefore, MQTT PING Request messages may be sent
* at an incorrect time interval to the broker. If the broker does
* not receive a ping request within 1.5x the time sent in a
* connection request, the broker may close the connection.
* To enable the keep alive feature, set this value
* to the desired interval in seconds.
*/
#define shadowexampleKEEP_ALIVE_SECONDS ( 0 )
/**
* @brief The timeout for MQTT operations in this example.
*/
#define shadowexampleMQTT_TIMEOUT_MS ( 5000 )
/**
* @brief Update count of shadow in a loop in the demo.
*
*/
#define shadowexampleUPDATE_COUNT ( 20 )
/**
* @brief The task wait period between each Shadow update.
*
*/
#define shadowexampleUPDATE_PERIOD_MS ( 3000 )
/**
* @brief The task wait period between each demo loop.
*/
#define shadowexampleLOOP_WAIT_PERIOD_MS ( 5000 )
/**
* @brief The timeout period for updates from Shadow Delta Callback before
* attempting next Shadow Update.
*/
#define shadowexampleWAIT_PERIOD_FOR_DELTA_MS ( 5000 )
/**
* @brief Argument for AwsIotShadow_Init to use the default timeout.
*/
#define shadowexampleUSE_DEFAULT_MQTT_TIMEOUT ( 0 )
/**
* @brief The bit which is set in the demo task's notification value from the
* disconnect callback to inform the demo task about the MQTT disconnect.
*/
#define shadowexampleDISCONNECTED_BIT ( 1UL << 0UL )
/**
* @brief Compile time calculation of shadowexampleCLIENT_IDENTIFIER_LENGTH.
*/
#define shadowexampleCLIENT_IDENTIFIER_LENGTH sizeof( awsiotdemoprofileCLIENT_IDENTIFIER ) - 1
/**
* @brief Format string representing a Shadow document with a "desired" state.
*
* Note the client token, which is required for all Shadow updates. The client
* token must be unique at any given time, but may be reused once the update is
* completed. For this demo, a timestamp is used for a client token.
*/
#define shadowexampleDESIRED_JSON \
"{" \
"\"state\":{" \
"\"desired\":{" \
"\"powerOn\":%01d" \
"}" \
"}," \
"\"clientToken\":\"%06lu\"" \
"}"
/**
* @brief The expected size of #shadowexampleDESIRED_JSON.
*
* Because all the format specifiers in #shadowexampleDESIRED_JSON include a
* length, its full size is known at compile-time.
*/
#define shadowexampleDESIRED_JSON_SIZE ( sizeof( shadowexampleDESIRED_JSON ) - 3 )
/**
* @brief Format string representing a Shadow document with a "reported" state.
*
* Note the client token, which is required for all Shadow updates. The client
* token must be unique at any given time, but may be reused once the update is
* completed. For this demo, a timestamp is used for a client token.
*/
#define shadowexampleREPORTED_JSON \
"{" \
"\"state\":{" \
"\"reported\":{" \
"\"powerOn\":%01d" \
"}" \
"}," \
"\"clientToken\":\"%06lu\"" \
"}"
/**
* @brief The expected size of #shadowexampleREPORTED_JSON.
*
* Because all the format specifiers in #shadowexampleREPORTED_JSON include a
* length, its full size is known at compile-time.
*/
#define shadowexampleREPORTED_JSON_SIZE ( sizeof( shadowexampleREPORTED_JSON ) - 3 )
/**
* @brief This is the current state of the shadow used in this demo.
*/
static int32_t lDevicePowerOnState = 0;
/**
* @brief This is a Semaphore used to synchronize between delta callback and
* Shadow updates.
*/
iot_sem_internal_t xDeltaSemaphore = { 0 };
/**
* @brief The task used to demonstrate Shadow.
*
* @param[in] pvParameters Parameters as passed at the time of task creation. Not
* used in this example.
*
*/
static void prvShadowDemoTask( void *pvParameters );
/**
* @brief The callback invoked by the MQTT library when the MQTT connection gets
* disconnected.
*
* @param[in] pvCallbackContext Callback context as provided at the time of
* connect.
* @param[in] pxCallbackParams Contains the reason why the MQTT connection was
* disconnected.
*/
static void prvExample_OnDisconnect( void * pvCallbackContext,
IotMqttCallbackParam_t * pxCallbackParams );
/**
* @brief The callback invoked by the MQTT library when a message is received on
* a subscribed topic from the MQTT broker.
*
* @param[in] pvCallbackContext Callback context as provided at the time of
* subscribe.
* @param[in] pxCallbackParams Contain the details about the received message -
* topic on which the message was received, the received message.
*/
static void prvExample_OnMessageReceived( void * pvCallbackContext,
IotMqttCallbackParam_t * pxCallbackParams );
/**
* @brief Connects to the MQTT broker as specified in awsiotdemoprofileAWS_ENDPOINT
* and awsiotdemoprofileAWS_MQTT_PORT.
*
*/
static void prvMQTTConnect( void );
/**
* @brief Disconnects from the MQTT broker gracefully by sending an MQTT
* DISCONNECT message.
*
*/
static void prvMQTTDisconnect( void );
/**
* @brief Initializes the IoT libraries used by this demo.
*/
static void prvInitialiseLibraries( void );
/**
* @brief The MQTT connection handle used in this example.
*/
static IotMqttConnection_t xMQTTConnection = IOT_MQTT_CONNECTION_INITIALIZER;
/**
* @brief Set the Shadow callback functions used in this demo.
*
* There are 2 Shadow callbacks set by this function. updated callback
* and delta Callback.
*
*/
static void prvSetShadowCallbacks( void );
/**
* @brief Clear the Shadow callbacks.
*
* There are 2 Shadow callbacks cleared by this function. updated callback
* and delta Callback.
*
*/
static void prvClearShadowCallbacks( void );
/**
* @brief Try to delete any Shadow document in the cloud.
*
*/
static void prvClearShadowDocument( void );
/**
* @brief Send the Shadow updates that will trigger the Shadow callbacks.
*
*/
static void prvSendShadowUpdates( void );
/**
* @brief Shadow delta callback, invoked when the desired and updates Shadow
* states differ.
*
* This function simulates a device updating its state in response to a Shadow.
*
* @param[in] pCallbackContext Delta semaphore used to synchronize between
* delta callback and updated callback.
* @param[in] pxCallbackParam The received Shadow delta document.
*/
static void prvShadowDeltaCallback( void * pCallbackContext,
AwsIotShadowCallbackParam_t * pxCallbackParam );
/**
* @brief Shadow updated callback, invoked when the Shadow document changes.
*
* This function reports when a Shadow has been updated.
*
* @param[in] pCallbackContext Not used.
* @param[in] pxCallbackParam The received Shadow updated document.
*/
static void prvShadowUpdatedCallback( void * pCallbackContext,
AwsIotShadowCallbackParam_t * pxCallbackParam );
/**
* @brief Parses a key in the "state" section of a Shadow delta document.
*
* @param[in] pcDeltaDocument The Shadow delta document to parse.
* @param[in] xDeltaDocumentLength The length of `pcDeltaDocument`.
* @param[in] pcDeltaKey The key in the delta document to find. Must be NULL-terminated.
* @param[out] ppcDelta Set to the first character in the delta key.
* @param[out] pxDeltaLength The length of the delta key.
*
* @return `true` if the given delta key is found; `false` otherwise.
*/
static BaseType_t prvGetDelta( const char * pcDeltaDocument,
size_t xDeltaDocumentLength,
const char * pcDeltaKey,
const char ** ppcDelta,
size_t * pxDeltaLength );
/**
* @brief Parses the "state" key from the "previous" or "current" sections of a
* Shadow updated document.
*
* @param[in] pcUpdatedDocument The Shadow updated document to parse.
* @param[in] xUpdatedDocumentLength The length of `pcUpdatedDocument`.
* @param[in] pcSectionKey Either "previous" or "current". Must be NULL-terminated.
* @param[out] ppcState Set to the first character in "state".
* @param[out] pxStateLength Length of the "state" section.
*
* @return pdTRUE if the "state" was found; pdFALSE otherwise.
*/
static BaseType_t prvGetUpdatedState( const char * pcUpdatedDocument,
size_t xUpdatedDocumentLength,
const char * pcSectionKey,
const char ** ppcState,
size_t * pxStateLength );
/*-----------------------------------------------------------*/
/**
* @brief Parameters used to create the system task pool.
*/
static const IotTaskPoolInfo_t xTaskPoolParameters =
{
/* Minimum number of threads in a task pool.
* Note the slimmed down version of the task
* pool used by this library does not auto-scale
* the number of tasks in the pool so in this
* case this sets the number of tasks in the
* pool. */
1,
/* Maximum number of threads in a task pool.
* Note the slimmed down version of the task
* pool used by this library does not auto-scale
* the number of tasks in the pool so in this
* case this parameter is just ignored. */
1,
/* Stack size for every task pool thread - in
* bytes, hence multiplying by the number of bytes
* in a word as configMINIMAL_STACK_SIZE is
* specified in words. */
configMINIMAL_STACK_SIZE * sizeof( portSTACK_TYPE ),
/* Priority for every task pool thread. */
tskIDLE_PRIORITY,
};
/*-----------------------------------------------------------*/
static const struct IotNetworkServerInfo xMQTTBrokerInfo =
{
.pHostName = awsiotdemoprofileAWS_ENDPOINT,
.port = awsiotdemoprofileAWS_MQTT_PORT
};
static struct IotNetworkCredentials xNetworkSecurityCredentials =
{
/* Optional TLS extensions. For this demo, they are disabled. */
.pAlpnProtos = NULL,
.maxFragmentLength = 0,
/* SNI is enabled by default. */
.disableSni = false,
/* Provide the certificate for validating the server. Only required for
demos using TLS. */
.pRootCa = awsiotdemoprofileAWS_CERTIFICATE_PEM,
.rootCaSize = sizeof( awsiotdemoprofileAWS_CERTIFICATE_PEM ),
/* Strong mutual authentication to authenticate both the broker and
* the client. */
.pClientCert = awsiotdemoprofileCLIENT_CERTIFICATE_PEM,
.clientCertSize = sizeof( awsiotdemoprofileCLIENT_CERTIFICATE_PEM ),
.pPrivateKey = awsiotdemoprofileCLIENT_PRIVATE_KEY_PEM,
.privateKeySize = sizeof( awsiotdemoprofileCLIENT_PRIVATE_KEY_PEM )
};
static IotMqttNetworkInfo_t xNetworkInfo =
{
/* No connection to the MQTT broker has been established yet and we want to
* establish a new connection. */
.createNetworkConnection = true,
.u.setup.pNetworkServerInfo = &( xMQTTBrokerInfo ),
/* Set the TLS credentials for the new MQTT connection. This member is NULL
* for the plain text MQTT demo. */
.u.setup.pNetworkCredentialInfo = &xNetworkSecurityCredentials,
/* Use FreeRTOS+TCP network interface. */
.pNetworkInterface = IOT_NETWORK_INTERFACE_FREERTOS,
/* Setup the callback which is called when the MQTT connection is
* disconnected. The task handle is passed as the callback context which
* is used by the callback to send a task notification to this task.*/
.disconnectCallback.function = prvExample_OnDisconnect
};
static const IotMqttConnectInfo_t xConnectInfo =
{
/* Set this flag to true if connecting to the AWS IoT MQTT broker. */
.awsIotMqttMode = true,
/* Start with a clean session i.e. direct the MQTT broker to discard any
* previous session data. Also, establishing a connection with clean session
* will ensure that the broker does not store any data when this client
* gets disconnected. */
.cleanSession = true,
/* Since we are starting with a clean session, there are no previous
* subscriptions to be restored. */
.pPreviousSubscriptions = NULL,
.previousSubscriptionCount = 0,
/* We do not want to publish Last Will and Testament (LWT) message if the
* client gets disconnected. */
.pWillInfo = NULL,
/* Send an MQTT PING request every minute to keep the connection open if
there is no other MQTT traffic. */
.keepAliveSeconds = shadowexampleKEEP_ALIVE_SECONDS,
/* The client identifier is used to uniquely identify this MQTT client to
* the MQTT broker. In a production device the identifier can be something
* unique, such as a device serial number. */
.pClientIdentifier = awsiotdemoprofileCLIENT_IDENTIFIER,
.clientIdentifierLength = ( uint16_t ) sizeof( awsiotdemoprofileCLIENT_IDENTIFIER ) - 1,
/* This example does not authenticate the client and therefore username and
* password fields are not used. */
.pUserName = NULL,
.userNameLength = 0,
.pPassword = NULL,
.passwordLength = 0
};
/*-----------------------------------------------------------*/
void vStartShadowDeviceOperationsDemo( void )
{
TickType_t xShortDelay = ( TickType_t ) pdMS_TO_TICKS( ( TickType_t ) 500 );
/* Wait a short time to allow receipt of the ARP replies. */
vTaskDelay( xShortDelay );
/* This example uses a single application task, which in turn is used to
* connect, subscribe, publish, unsubscribe and disconnect from the MQTT
* broker. */
xTaskCreate( prvShadowDemoTask, /* Function that implements the task. */
"ShadowDemo", /* Text name for the task - only used for debugging. */
democonfigDEMO_STACKSIZE,/* Size of stack (in words, not bytes) to allocate for the task. */
NULL, /* Task parameter - not used in this case. */
tskIDLE_PRIORITY, /* Task priority, must be between 0 and configMAX_PRIORITIES - 1. */
NULL ); /* Used to pass out a handle to the created task - not used in this case. */
}
/*-----------------------------------------------------------*/
static void prvShadowDemoTask( void *pvParameters )
{
uint32_t ulNotificationValue = 0;
const TickType_t xNoDelay = ( TickType_t ) 0;
/* Remove compiler warnings about unused parameters. */
( void ) pvParameters;
/* One time initialization of the libraries used by this demo. */
prvInitialiseLibraries();
for( ; ; )
{
/* Don't expect any notifications to be pending yet. */
configASSERT( ulTaskNotifyTake( pdTRUE, xNoDelay ) == 0 );
/****************************** Connect. ******************************/
/* Establish a connection to the AWS IoT MQTT broker. This example connects to
* the MQTT broker as specified in awsiotdemoprofileAWS_ENDPOINT and
* awsiotdemoprofileAWS_MQTT_PORT at the top of this file.
*/
configPRINTF( ( "Attempt to connect to %s\r\n", awsiotdemoprofileAWS_ENDPOINT ) );
prvMQTTConnect();
configPRINTF( ( "Connected to %s\r\n", awsiotdemoprofileAWS_ENDPOINT ) );
/************************ Create a semaphore **************************/
/* Creates a semaphore to synchronize between delta callback and
* Shadow updates.
*/
configPRINTF( ( "Creating delta semaphore\r\n" ) );
configASSERT( xSemaphoreCreateCountingStatic( 1, 0, &xDeltaSemaphore.xSemaphore ) != NULL );
/************************ Set shadow callbacks ************************/
/* Sets the updated callback and delta callback */
configPRINTF( ( "Setting the updated callback and delta callback\r\n" ) );
prvSetShadowCallbacks();
/************************ Clear shadow document ***********************/
/* Clears the Shadow document if it exists already */
configPRINTF( ( "Clearing the Shadow document if it already exits\r\n" ) );
prvClearShadowDocument();
/*********************** Send Shadow updates **************************/
/* Send Shadow updates for shadowexampleUPDATE_COUNT times.
* For each Shadow update, it waits on xDeltaSemaphore. xDeltaSemaphore
* will be posted by the delta callback.
*/
configPRINTF( ( "Sending Shadow updates\r\n" ) );
prvSendShadowUpdates();
/************************ Clear shadow document ***********************/
/* Clears the Shadow document at the end of the demo */
configPRINTF( ( "Clearing the Shadow document\r\n" ) );
prvClearShadowDocument();
/************** Clear callbacks and Disconnect MQTT. ******************/
/* Clear updated callback and delta callback */
configPRINTF( ( "Clearing the Shadow updated callback and delta callback\r\n" ) );
prvClearShadowCallbacks();
/* Disconnect MQTT gracefully. */
prvMQTTDisconnect();
configPRINTF( ( "Disconnected from %s\r\n\r\n", awsiotdemoprofileAWS_ENDPOINT ) );
/* Wait for the disconnect operation to complete which is informed to us
* by the disconnect callback (prvExample_OnDisconnect)by setting
* the shadowexampleDISCONNECTED_BIT in this task's notification value.
* Note that the bit is cleared in the task's notification value to
* ensure that it is ready for the next run. */
xTaskNotifyWait( 0UL, /* Don't clear any bits on entry. */
shadowexampleDISCONNECTED_BIT, /* Clear bit on exit. */
&( ulNotificationValue ), /* Obtain the notification value. */
pdMS_TO_TICKS( shadowexampleMQTT_TIMEOUT_MS ) );
configASSERT( ( ulNotificationValue & shadowexampleDISCONNECTED_BIT ) == shadowexampleDISCONNECTED_BIT );
/* Destroy the delta semaphore*/
vSemaphoreDelete( ( SemaphoreHandle_t ) &xDeltaSemaphore.xSemaphore );
/* Clear the current reported shadow state to toggle the reported state. */
lDevicePowerOnState = 0;
/* Wait for some time between two iterations to ensure that we do not
* bombard the broker. */
configPRINTF( ( "prvShadowDemoTask() completed an iteration successfully. Total free heap is %u\r\n", xPortGetFreeHeapSize() ) );
configPRINTF( ( "Demo completed successfully.\r\n" ) );
configPRINTF( ( "Short delay before starting the next iteration... \r\n\r\n" ) );
vTaskDelay( pdMS_TO_TICKS( shadowexampleLOOP_WAIT_PERIOD_MS ) );
}
}
/*-----------------------------------------------------------*/
static void prvExample_OnDisconnect( void * pvCallbackContext,
IotMqttCallbackParam_t * pxCallbackParams )
{
TaskHandle_t xDemoTaskHandle = ( TaskHandle_t ) pvCallbackContext;
/* Ensure that we initiated the disconnect. */
configASSERT( pxCallbackParams->u.disconnectReason == IOT_MQTT_DISCONNECT_CALLED );
/* Inform the demo task about the disconnect. */
xTaskNotify( xDemoTaskHandle,
shadowexampleDISCONNECTED_BIT,
eSetBits /* Set the shadowexampleDISCONNECTED_BIT in the demo task's notification value. */
);
}
/*-----------------------------------------------------------*/
static void prvMQTTConnect( void )
{
IotMqttError_t xResult;
/* Set the context to pass into the disconnect callback function. */
xNetworkInfo.disconnectCallback.pCallbackContext = ( void * ) xTaskGetCurrentTaskHandle();
/* Establish the connection to the MQTT broker - It is a blocking call and
* will return only when connection is complete or a timeout occurs. */
xResult = IotMqtt_Connect( &( xNetworkInfo ),
&( xConnectInfo ),
shadowexampleMQTT_TIMEOUT_MS,
&( xMQTTConnection ) );
configASSERT( xResult == IOT_MQTT_SUCCESS );
}
/*-----------------------------------------------------------*/
static void prvMQTTDisconnect( void )
{
/* Send a MQTT DISCONNECT packet to the MQTT broker to do a graceful
* disconnect. */
IotMqtt_Disconnect( xMQTTConnection,
0 /* flags - 0 means a graceful disconnect by sending MQTT DISCONNECT. */
);
}
/*-----------------------------------------------------------*/
static void prvSetShadowCallbacks( void )
{
AwsIotShadowError_t xCallbackStatus = AWS_IOT_SHADOW_STATUS_PENDING;
AwsIotShadowCallbackInfo_t xDeltaCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER,
xUpdatedCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
/* Set the functions for callbacks. */
xDeltaCallback.pCallbackContext = &xDeltaSemaphore;
xDeltaCallback.function = prvShadowDeltaCallback;
xUpdatedCallback.function = prvShadowUpdatedCallback;
/************************ Set delta callbacks ****************************/
/* Set the delta callback, which notifies of different desired and reported
* Shadow states. */
xCallbackStatus = AwsIotShadow_SetDeltaCallback( xMQTTConnection,
awsiotdemoprofileCLIENT_IDENTIFIER,
shadowexampleCLIENT_IDENTIFIER_LENGTH,
0, &xDeltaCallback );
configASSERT( xCallbackStatus == AWS_IOT_SHADOW_SUCCESS );
/************************ Set updated callbacks **************************/
/* Set the updated callback, which notifies when a Shadow document is
* changed. */
xCallbackStatus = AwsIotShadow_SetUpdatedCallback( xMQTTConnection,
awsiotdemoprofileCLIENT_IDENTIFIER,
shadowexampleCLIENT_IDENTIFIER_LENGTH,
0, &xUpdatedCallback );
configASSERT( xCallbackStatus == AWS_IOT_SHADOW_SUCCESS );
}
/*-----------------------------------------------------------*/
static void prvClearShadowCallbacks( void )
{
AwsIotShadowError_t xCallbackStatus = AWS_IOT_SHADOW_STATUS_PENDING;
/************************ Clear delta callbacks **************************/
xCallbackStatus = AwsIotShadow_SetDeltaCallback( xMQTTConnection,
awsiotdemoprofileCLIENT_IDENTIFIER,
shadowexampleCLIENT_IDENTIFIER_LENGTH,
0, NULL );
configASSERT( xCallbackStatus == AWS_IOT_SHADOW_SUCCESS );
/************************ Clear updated callbacks ************************/
xCallbackStatus = AwsIotShadow_SetUpdatedCallback( xMQTTConnection,
awsiotdemoprofileCLIENT_IDENTIFIER,
shadowexampleCLIENT_IDENTIFIER_LENGTH,
0, NULL );
configASSERT( xCallbackStatus == AWS_IOT_SHADOW_SUCCESS );
}
/*-----------------------------------------------------------*/
static void prvShadowDeltaCallback( void * pCallbackContext,
AwsIotShadowCallbackParam_t * pxCallbackParam )
{
BaseType_t xDeltaFound = pdFALSE;
const char * pcDelta = NULL;
size_t xDeltaLength = 0;
IotSemaphore_t * pxDeltaSemaphore = pCallbackContext;
uint32_t ulUpdateDocumentLength = 0;
AwsIotShadowError_t xShadowStatus = AWS_IOT_SHADOW_STATUS_PENDING;
AwsIotShadowDocumentInfo_t xUpdateDocument = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
uint8_t ucNewState = 0;
configASSERT( pxDeltaSemaphore != NULL );
configASSERT( pxCallbackParam != NULL );
/* A buffer containing the update document. It has static duration to prevent
* it from being placed on the call stack.This is only safe because there
* is only one task in the task pool so this function cannot be called from
* two tasks simultaneously. */
static char cUpdateDocument[ shadowexampleREPORTED_JSON_SIZE + 1 ] = { 0 };
/****************** Get delta state from Shadow document *****************/
/* Check if there is a different "powerOn" state in the Shadow. */
xDeltaFound = prvGetDelta( pxCallbackParam->u.callback.pDocument,
pxCallbackParam->u.callback.documentLength,
"powerOn",
&pcDelta,
&xDeltaLength );
configASSERT( xDeltaFound == pdTRUE );
/* Change the current state based on the value in the delta document. */
if( *pcDelta == '0' )
{
ucNewState = 0;
}
else if( *pcDelta == '1' )
{
ucNewState = 1;
}
else
{
configPRINTF( ( "Unknown powerOn state parsed from delta document.\r\n" ) );
/* Set new state to current state to ignore the delta document. */
ucNewState = lDevicePowerOnState;
}
if( ucNewState != lDevicePowerOnState )
{
/* Toggle state. */
configPRINTF( ( "%.*s changing state from %d to %d.\r\n",
pxCallbackParam->thingNameLength,
pxCallbackParam->pThingName,
lDevicePowerOnState,
ucNewState ) );
lDevicePowerOnState = ucNewState;
/* Set the common members to report the new state. */
xUpdateDocument.pThingName = pxCallbackParam->pThingName;
xUpdateDocument.thingNameLength = pxCallbackParam->thingNameLength;
xUpdateDocument.u.update.pUpdateDocument = cUpdateDocument;
xUpdateDocument.u.update.updateDocumentLength = shadowexampleREPORTED_JSON_SIZE;
/* Generate a Shadow document for the reported state. To keep the client
* token within 6 characters, it is modded by 1000000. */
ulUpdateDocumentLength = snprintf( cUpdateDocument,
shadowexampleREPORTED_JSON_SIZE + 1,
shadowexampleREPORTED_JSON,
( int ) lDevicePowerOnState,
( long unsigned ) ( IotClock_GetTimeMs() % 1000000 ) );
/* Check if the reported state document is generated for Shadow update*/
configASSERT( ( size_t ) ulUpdateDocumentLength == shadowexampleREPORTED_JSON_SIZE );
/* Send the Shadow update. Its result is not checked by waiting for the
* callback, as the Shadow updated callback will report if the Shadow
* was successfully updated. As the Shadow is constantly updated
* in this demo, the "Keep Subscriptions" flag is passed to this
* function. */
xShadowStatus = AwsIotShadow_UpdateAsync( pxCallbackParam->mqttConnection,
&xUpdateDocument,
AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS,
NULL,
NULL );
configASSERT( xShadowStatus == AWS_IOT_SHADOW_STATUS_PENDING );
configPRINTF( ( "%.*s sent new state report: %.*s\r\n",
pxCallbackParam->thingNameLength,
pxCallbackParam->pThingName,
shadowexampleREPORTED_JSON_SIZE,
cUpdateDocument ) );
/* Post to the delta semaphore to unblock the thread sending Shadow updates. */
xSemaphoreGive( ( SemaphoreHandle_t ) &pxDeltaSemaphore->xSemaphore );
}
}
/*-----------------------------------------------------------*/
static void prvShadowUpdatedCallback( void * pCallbackContext,
AwsIotShadowCallbackParam_t * pxCallbackParam )
{
BaseType_t xPreviousFound = pdFALSE, xCurrentFound = pdFALSE;
const char * pcPrevious = NULL, * pcCurrent = NULL;
size_t xPreviousLength = 0, xCurrentLength = 0;
/* Silence warnings about unused parameters. */
( void ) pCallbackContext;
configASSERT( pxCallbackParam != NULL );
/****************** Get previous state from Shadow document **************/
/* Find the previous Shadow document. */
xPreviousFound = prvGetUpdatedState( pxCallbackParam->u.callback.pDocument,
pxCallbackParam->u.callback.documentLength,
"previous",
&pcPrevious,
&xPreviousLength );
/****************** Get current state from Shadow document **************/
/* Find the current Shadow document. */
xCurrentFound = prvGetUpdatedState( pxCallbackParam->u.callback.pDocument,
pxCallbackParam->u.callback.documentLength,
"current",
&pcCurrent,
&xCurrentLength );
configASSERT( ( xPreviousFound == pdTRUE ) || ( xCurrentFound == pdTRUE ) );
/* Log the previous and current states. */
configPRINTF( ( "Shadow was updated!\r\n"
"Previous: {\"state\":%.*s}\r\n"
"Current: {\"state\":%.*s}\r\n",
xPreviousLength,
pcPrevious,
xCurrentLength,
pcCurrent ) );
}
/*-----------------------------------------------------------*/
static BaseType_t prvGetDelta( const char * pcDeltaDocument,
size_t xDeltaDocumentLength,
const char * pcDeltaKey,
const char ** pcDelta,
size_t * pcDeltaLength )
{
BaseType_t xStateFound = pdFALSE, xDeltaFound = pdFALSE;
const size_t xDeltaKeyLength = strlen( pcDeltaKey );
const char * pcState = NULL;
size_t xStateLength = 0;
configASSERT( pcDeltaDocument != NULL );
configASSERT( pcDeltaKey != NULL );
/****************** Get state from Shadow document ***********************/
/* Note: This parser used is specific for parsing AWS IoT document received
* through a mutually authenticated connection. This parser will not check
* for the correctness of the document as it is designed for low memory
* footprint rather than checking for correctness of the document. This
* parser is not meant to be used as a general purpose JSON parser.
*/
xStateFound = ( BaseType_t ) AwsIotDocParser_FindValue(
pcDeltaDocument,
xDeltaDocumentLength,
"state",
5,
&pcState,
&xStateLength );
configASSERT( xStateFound == pdTRUE );
/********** Get delta key from state section of Shadow document **********/
/* Note: This parser used is specific for parsing AWS IoT document received
* through a mutually authenticated connection. This parser will not check
* for the correctness of the document as it is designed for low memory
* footprint rather than checking for correctness of the document. This
* parser is not meant to be used as a general purpose JSON parser.
*/
xDeltaFound = ( BaseType_t ) AwsIotDocParser_FindValue(
pcState,
xStateLength,
pcDeltaKey,
xDeltaKeyLength,
pcDelta,
pcDeltaLength );
return xDeltaFound;
}
/*-----------------------------------------------------------*/
static BaseType_t prvGetUpdatedState( const char * pcUpdatedDocument,
size_t xUpdatedDocumentLength,
const char * pcSectionKey,
const char ** ppcState,
size_t * ppcStateLength )
{
BaseType_t xSectionFound = pdFALSE, xStateFound = pdFALSE;
const size_t xSectionKeyLength = strlen( pcSectionKey );
const char * pcSection = NULL;
size_t xSectionLength = 0;
configASSERT( pcUpdatedDocument != NULL );
configASSERT( pcSectionKey != NULL );
/*********** Find the given section in the updated document. *************/
/* Note: This parser used is specific for parsing AWS IoT document received
* through a mutually authenticated connection. This parser will not check
* for the correctness of the document as it is designed for low memory
* footprint rather than checking for correctness of the document. This
* parser is not meant to be used as a general purpose JSON parser.
*/
xSectionFound = ( BaseType_t ) AwsIotDocParser_FindValue(
pcUpdatedDocument,
xUpdatedDocumentLength,
pcSectionKey,
xSectionKeyLength,
&pcSection,
&xSectionLength );
configASSERT( xSectionFound == pdTRUE );
/*********** Find the state key within the section found *****************/
/* Find the "state" key within the "previous" or "current" section.
*
* Note: This parser used is specific for parsing AWS IoT document received
* through a mutually authenticated connection. This parser will not check
* for the correctness of the document as it is designed for low memory
* footprint rather than checking for correctness of the document. This
* parser is not meant to be used as a general purpose JSON parser.
*/
xStateFound = ( BaseType_t ) AwsIotDocParser_FindValue(
pcSection,
xSectionLength,
"state",
5,
ppcState,
ppcStateLength );
return xStateFound;
}
/*-----------------------------------------------------------*/
static void prvClearShadowDocument( void )
{
AwsIotShadowError_t xDeleteStatus = AWS_IOT_SHADOW_STATUS_PENDING;
/************************* Delete Shadow document ************************/
xDeleteStatus = AwsIotShadow_DeleteSync( xMQTTConnection,
awsiotdemoprofileCLIENT_IDENTIFIER,
shadowexampleCLIENT_IDENTIFIER_LENGTH,
0, shadowexampleMQTT_TIMEOUT_MS );
configASSERT( ( xDeleteStatus == AWS_IOT_SHADOW_SUCCESS ) || ( xDeleteStatus == AWS_IOT_SHADOW_NOT_FOUND ) );
configPRINTF( ( "Successfully cleared Shadow of %.*s.\r\n",
shadowexampleCLIENT_IDENTIFIER_LENGTH,
awsiotdemoprofileCLIENT_IDENTIFIER ) );
}
/*-----------------------------------------------------------*/
static void prvSendShadowUpdates( void )
{
int32_t lIndex = 0, lDesiredState = 0, lStatus = 0;
AwsIotShadowError_t xShadowStatus = AWS_IOT_SHADOW_STATUS_PENDING;
AwsIotShadowDocumentInfo_t xUpdateDocument = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
/* A buffer containing the update document. It has static duration to prevent
* it from being placed on the call stack. */
static char cUpdateDocument[ shadowexampleDESIRED_JSON_SIZE + 1 ] = { 0 };
/********** Set the common members of Shadow update document *************/
xUpdateDocument.pThingName = awsiotdemoprofileCLIENT_IDENTIFIER;
xUpdateDocument.thingNameLength = shadowexampleCLIENT_IDENTIFIER_LENGTH;
xUpdateDocument.u.update.pUpdateDocument = cUpdateDocument;
xUpdateDocument.u.update.updateDocumentLength = shadowexampleDESIRED_JSON_SIZE;
/*************** Publish Shadow updates at a set period. *****************/
for( lIndex = 1; lIndex <= shadowexampleUPDATE_COUNT; lIndex++ )
{
/* Toggle the desired state. */
lDesiredState = !( lDesiredState );
/* Generate a Shadow desired state document, using a timestamp for the client
* token. To keep the client token within 6 characters, it is modded by 1000000. */
lStatus = snprintf( cUpdateDocument,
shadowexampleDESIRED_JSON_SIZE + 1,
shadowexampleDESIRED_JSON,
( int ) lDesiredState,
( long unsigned ) ( IotClock_GetTimeMs() % 1000000 ) );
/* Check for errors from snprintf. The expected value is the length of
* the desired JSON document less the format specifier for the state. */
configASSERT( lStatus == shadowexampleDESIRED_JSON_SIZE );
configPRINTF( ( "Sending Shadow update %d of %d: %s\r\n",
lIndex,
shadowexampleUPDATE_COUNT,
cUpdateDocument ) );
/* Send the Shadow update. Because the Shadow is constantly updated in
* this demo, the "Keep Subscriptions" flag is passed to this function.
* Note that this flag only needs to be passed on the first call, but
* passing it for subsequent calls is fine.
*/
xShadowStatus = AwsIotShadow_UpdateSync( xMQTTConnection,
&xUpdateDocument,
AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS,
shadowexampleMQTT_TIMEOUT_MS );
configASSERT( xShadowStatus == AWS_IOT_SHADOW_SUCCESS );
configPRINTF( ( "Successfully sent Shadow update %d of %d.\r\n",
lIndex,
shadowexampleUPDATE_COUNT ) );
/* Wait for the delta callback to change its state before continuing. */
configASSERT( xSemaphoreTake( ( SemaphoreHandle_t ) &xDeltaSemaphore.xSemaphore,
pdMS_TO_TICKS( shadowexampleWAIT_PERIOD_FOR_DELTA_MS ) ) == pdTRUE );
IotClock_SleepMs( shadowexampleUPDATE_PERIOD_MS );
}
/* Remove persistent subscriptions. In the AwsIotShadow_UpdateSync call, we have used the */
xShadowStatus = AwsIotShadow_RemovePersistentSubscriptions( xMQTTConnection,
awsiotdemoprofileCLIENT_IDENTIFIER,
shadowexampleCLIENT_IDENTIFIER_LENGTH,
AWS_IOT_SHADOW_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS );
configASSERT( xShadowStatus == AWS_IOT_SHADOW_SUCCESS );
}
/*-----------------------------------------------------------*/
static void prvInitialiseLibraries( void )
{
IotTaskPoolError_t xTaskPoolResult;
IotMqttError_t xResult;
IotNetworkError_t xNetworkResult;
/* The MQTT library needs a task pool, so create the system task pool. */
xTaskPoolResult = IotTaskPool_CreateSystemTaskPool( &( xTaskPoolParameters ) );
configASSERT( xTaskPoolResult == IOT_TASKPOOL_SUCCESS );
/* Initialize the network stack abstraction for FreeRTOS. */
xNetworkResult = IotNetworkFreeRTOS_Init();
configASSERT( xNetworkResult == IOT_NETWORK_SUCCESS );
/* MQTT library must be initialized before it can be used. This is just one
* time initialization. */
xResult = IotMqtt_Init();
configASSERT( xResult == IOT_MQTT_SUCCESS );
/* Initialize Shadow library*/
xResult = AwsIotShadow_Init( shadowexampleUSE_DEFAULT_MQTT_TIMEOUT );
configASSERT( xResult == AWS_IOT_SHADOW_SUCCESS );
}
/*-----------------------------------------------------------*/