summaryrefslogtreecommitdiff
path: root/FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/shadow/shadow_device_operations/DemoTasks/ShadowDeviceOperationsExamples.c
diff options
context:
space:
mode:
Diffstat (limited to 'FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/shadow/shadow_device_operations/DemoTasks/ShadowDeviceOperationsExamples.c')
-rw-r--r--FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/shadow/shadow_device_operations/DemoTasks/ShadowDeviceOperationsExamples.c1065
1 files changed, 1065 insertions, 0 deletions
diff --git a/FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/shadow/shadow_device_operations/DemoTasks/ShadowDeviceOperationsExamples.c b/FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/shadow/shadow_device_operations/DemoTasks/ShadowDeviceOperationsExamples.c
new file mode 100644
index 000000000..614e2614c
--- /dev/null
+++ b/FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/shadow/shadow_device_operations/DemoTasks/ShadowDeviceOperationsExamples.c
@@ -0,0 +1,1065 @@
+/*
+ * FreeRTOS Kernel V10.2.1
+ * Copyright (C) 2019 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 );
+}
+/*-----------------------------------------------------------*/