2 * FreeRTOS Kernel V10.3.0
\r
3 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
6 * this software and associated documentation files (the "Software"), to deal in
\r
7 * the Software without restriction, including without limitation the rights to
\r
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
9 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
10 * subject to the following conditions:
\r
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\r
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
22 * http://www.FreeRTOS.org
\r
23 * http://aws.amazon.com/freertos
\r
25 * 1 tab == 4 spaces!
\r
28 /* A device's Shadow is a JSON document that is used to store and retrieve
\r
29 * current state information for a device in AWS (Amazon Web Services) Cloud.
\r
30 * Please refer to the AWS documentation for more details about Device Shadow
\r
32 * https://docs.aws.amazon.com/iot/latest/developerguide/iot-device-shadows.html
\r
34 * This demo creates a single application task that loops through a set of
\r
35 * examples that demonstrates usage of Shadow library. Please find the list of
\r
36 * Shadow operations and callbacks used in this demo below.
\r
37 * Shadow Delete - Deletes the Shadow document of the device.
\r
38 * Shadow Update - Updates the Shadow document with JSON document
\r
40 * Shadow Delta Callback - Callback to be invoked when Shadow document is
\r
41 * updated with different desired and reported
\r
43 * Shadow Updated Callback - Callback to be invoked when a Shadow document is
\r
46 * The demo program uses Shadow Update and Shadow Delta Callbacks to simulate
\r
47 * toggling a remote device's state. It sends a Shadow Update with a new
\r
48 * desired state and waits for the device to change its reported state in
\r
49 * Shadow Delta Callback as a response to the new desired state. In addition,
\r
50 * a Shadow Updated Callback is used to print the changing Shadow states.
\r
51 * Shadow Updates are done #shadowexampleUPDATE_COUNT times in each loop.At the
\r
52 * end of each loop Shadow document is deleted using Shadow Delete.
\r
54 * In this demo both update to the desired state of Shadow document and
\r
55 * response to it by updating the reported state is done in the same
\r
56 * application. This is to illustrate the capability of the Shadow service and
\r
57 * library in a single demo. Ideally the update to the desired state can be
\r
58 * from an external application to control the state of the device. This demo
\r
59 * is meant to demonstrate the usage of different Shadow library APIs.
\r
61 * The Shadow demo uses a MQTT connection to connect to Shadow Service in AWS.
\r
62 * Mutual authentication between AWS IoT MQTT Broker and Device is required to
\r
63 * run this demo. Please complete configurations in aws_iot_demo_profile.h for
\r
64 * mutual authentication.
\r
65 * More details for mutual authentication configuration can be found at
\r
66 * https://www.freertos.org/mqtt/preconfiguredexamplesMA.html
\r
68 * Note: The parser used in this demo is specific for parsing AWS IoT document
\r
69 * received through a mutually authenticated connection with AWS IoT MQTT
\r
70 * broker. This parser will not check for the correctness of the document as it
\r
71 * is designed for low memory footprint rather than checking for correctness of
\r
72 * the JSON document. This parser is not meant to be used as a general purpose
\r
76 /* Standard includes. */
\r
80 /* Kernel includes. */
\r
81 #include "FreeRTOS.h"
\r
85 /* FreeRTOS+TCP includes. */
\r
86 #include "FreeRTOS_IP.h"
\r
88 /* IoT SDK includes. */
\r
89 #include "aws_iot_shadow.h"
\r
90 #include "aws_iot_demo_profile.h"
\r
91 #include "iot_mqtt.h"
\r
92 #include "iot_taskpool_freertos.h"
\r
93 #include "aws_iot_doc_parser.h"
\r
94 #include "platform/iot_clock.h"
\r
95 #include "platform/iot_threads.h"
\r
96 #include "platform/iot_network_freertos.h"
\r
98 /* Preprocessor check iot configuration */
\r
99 #include "aws_iot_setup_check.h"
\r
101 /* Demo specific includes. */
\r
102 #include "demo_config.h"
\r
105 * @brief The keep-alive interval used for this example.
\r
107 * An MQTT ping request will be sent periodically at this interval.
\r
109 * @note: This value is set to zero to disable MQTT
\r
110 * keep alive for the Windows simulator project.
\r
111 * The FreeRTOS kernel does not accurately calculate time for the Windows
\r
112 * Simulator. Therefore, MQTT PING Request messages may be sent
\r
113 * at an incorrect time interval to the broker. If the broker does
\r
114 * not receive a ping request within 1.5x the time sent in a
\r
115 * connection request, the broker may close the connection.
\r
116 * To enable the keep alive feature, set this value
\r
117 * to the desired interval in seconds.
\r
119 #define shadowexampleKEEP_ALIVE_SECONDS ( 0 )
\r
122 * @brief The timeout for MQTT operations in this example.
\r
124 #define shadowexampleMQTT_TIMEOUT_MS ( 5000 )
\r
127 * @brief Update count of shadow in a loop in the demo.
\r
130 #define shadowexampleUPDATE_COUNT ( 20 )
\r
133 * @brief The task wait period between each Shadow update.
\r
136 #define shadowexampleUPDATE_PERIOD_MS ( 3000 )
\r
139 * @brief The task wait period between each demo loop.
\r
141 #define shadowexampleLOOP_WAIT_PERIOD_MS ( 5000 )
\r
144 * @brief The timeout period for updates from Shadow Delta Callback before
\r
145 * attempting next Shadow Update.
\r
147 #define shadowexampleWAIT_PERIOD_FOR_DELTA_MS ( 5000 )
\r
150 * @brief Argument for AwsIotShadow_Init to use the default timeout.
\r
152 #define shadowexampleUSE_DEFAULT_MQTT_TIMEOUT ( 0 )
\r
155 * @brief The bit which is set in the demo task's notification value from the
\r
156 * disconnect callback to inform the demo task about the MQTT disconnect.
\r
158 #define shadowexampleDISCONNECTED_BIT ( 1UL << 0UL )
\r
161 * @brief Compile time calculation of shadowexampleCLIENT_IDENTIFIER_LENGTH.
\r
163 #define shadowexampleCLIENT_IDENTIFIER_LENGTH sizeof( awsiotdemoprofileCLIENT_IDENTIFIER ) - 1
\r
166 * @brief Format string representing a Shadow document with a "desired" state.
\r
168 * Note the client token, which is required for all Shadow updates. The client
\r
169 * token must be unique at any given time, but may be reused once the update is
\r
170 * completed. For this demo, a timestamp is used for a client token.
\r
172 #define shadowexampleDESIRED_JSON \
\r
176 "\"powerOn\":%01d" \
\r
179 "\"clientToken\":\"%06lu\"" \
\r
183 * @brief The expected size of #shadowexampleDESIRED_JSON.
\r
185 * Because all the format specifiers in #shadowexampleDESIRED_JSON include a
\r
186 * length, its full size is known at compile-time.
\r
188 #define shadowexampleDESIRED_JSON_SIZE ( sizeof( shadowexampleDESIRED_JSON ) - 3 )
\r
191 * @brief Format string representing a Shadow document with a "reported" state.
\r
193 * Note the client token, which is required for all Shadow updates. The client
\r
194 * token must be unique at any given time, but may be reused once the update is
\r
195 * completed. For this demo, a timestamp is used for a client token.
\r
197 #define shadowexampleREPORTED_JSON \
\r
201 "\"powerOn\":%01d" \
\r
204 "\"clientToken\":\"%06lu\"" \
\r
208 * @brief The expected size of #shadowexampleREPORTED_JSON.
\r
210 * Because all the format specifiers in #shadowexampleREPORTED_JSON include a
\r
211 * length, its full size is known at compile-time.
\r
213 #define shadowexampleREPORTED_JSON_SIZE ( sizeof( shadowexampleREPORTED_JSON ) - 3 )
\r
216 * @brief This is the current state of the shadow used in this demo.
\r
218 static int32_t lDevicePowerOnState = 0;
\r
221 * @brief This is a Semaphore used to synchronize between delta callback and
\r
224 iot_sem_internal_t xDeltaSemaphore = { 0 };
\r
227 * @brief The task used to demonstrate Shadow.
\r
229 * @param[in] pvParameters Parameters as passed at the time of task creation. Not
\r
230 * used in this example.
\r
233 static void prvShadowDemoTask( void *pvParameters );
\r
236 * @brief The callback invoked by the MQTT library when the MQTT connection gets
\r
239 * @param[in] pvCallbackContext Callback context as provided at the time of
\r
241 * @param[in] pxCallbackParams Contains the reason why the MQTT connection was
\r
244 static void prvExample_OnDisconnect( void * pvCallbackContext,
\r
245 IotMqttCallbackParam_t * pxCallbackParams );
\r
248 * @brief The callback invoked by the MQTT library when a message is received on
\r
249 * a subscribed topic from the MQTT broker.
\r
251 * @param[in] pvCallbackContext Callback context as provided at the time of
\r
253 * @param[in] pxCallbackParams Contain the details about the received message -
\r
254 * topic on which the message was received, the received message.
\r
256 static void prvExample_OnMessageReceived( void * pvCallbackContext,
\r
257 IotMqttCallbackParam_t * pxCallbackParams );
\r
260 * @brief Connects to the MQTT broker as specified in awsiotdemoprofileAWS_ENDPOINT
\r
261 * and awsiotdemoprofileAWS_MQTT_PORT.
\r
264 static void prvMQTTConnect( void );
\r
267 * @brief Disconnects from the MQTT broker gracefully by sending an MQTT
\r
268 * DISCONNECT message.
\r
271 static void prvMQTTDisconnect( void );
\r
274 * @brief Initializes the IoT libraries used by this demo.
\r
276 static void prvInitialiseLibraries( void );
\r
279 * @brief The MQTT connection handle used in this example.
\r
281 static IotMqttConnection_t xMQTTConnection = IOT_MQTT_CONNECTION_INITIALIZER;
\r
284 * @brief Set the Shadow callback functions used in this demo.
\r
286 * There are 2 Shadow callbacks set by this function. updated callback
\r
287 * and delta Callback.
\r
290 static void prvSetShadowCallbacks( void );
\r
293 * @brief Clear the Shadow callbacks.
\r
295 * There are 2 Shadow callbacks cleared by this function. updated callback
\r
296 * and delta Callback.
\r
299 static void prvClearShadowCallbacks( void );
\r
302 * @brief Try to delete any Shadow document in the cloud.
\r
305 static void prvClearShadowDocument( void );
\r
308 * @brief Send the Shadow updates that will trigger the Shadow callbacks.
\r
311 static void prvSendShadowUpdates( void );
\r
314 * @brief Shadow delta callback, invoked when the desired and updates Shadow
\r
317 * This function simulates a device updating its state in response to a Shadow.
\r
319 * @param[in] pCallbackContext Delta semaphore used to synchronize between
\r
320 * delta callback and updated callback.
\r
321 * @param[in] pxCallbackParam The received Shadow delta document.
\r
323 static void prvShadowDeltaCallback( void * pCallbackContext,
\r
324 AwsIotShadowCallbackParam_t * pxCallbackParam );
\r
327 * @brief Shadow updated callback, invoked when the Shadow document changes.
\r
329 * This function reports when a Shadow has been updated.
\r
331 * @param[in] pCallbackContext Not used.
\r
332 * @param[in] pxCallbackParam The received Shadow updated document.
\r
334 static void prvShadowUpdatedCallback( void * pCallbackContext,
\r
335 AwsIotShadowCallbackParam_t * pxCallbackParam );
\r
338 * @brief Parses a key in the "state" section of a Shadow delta document.
\r
340 * @param[in] pcDeltaDocument The Shadow delta document to parse.
\r
341 * @param[in] xDeltaDocumentLength The length of `pcDeltaDocument`.
\r
342 * @param[in] pcDeltaKey The key in the delta document to find. Must be NULL-terminated.
\r
343 * @param[out] ppcDelta Set to the first character in the delta key.
\r
344 * @param[out] pxDeltaLength The length of the delta key.
\r
346 * @return `true` if the given delta key is found; `false` otherwise.
\r
348 static BaseType_t prvGetDelta( const char * pcDeltaDocument,
\r
349 size_t xDeltaDocumentLength,
\r
350 const char * pcDeltaKey,
\r
351 const char ** ppcDelta,
\r
352 size_t * pxDeltaLength );
\r
355 * @brief Parses the "state" key from the "previous" or "current" sections of a
\r
356 * Shadow updated document.
\r
358 * @param[in] pcUpdatedDocument The Shadow updated document to parse.
\r
359 * @param[in] xUpdatedDocumentLength The length of `pcUpdatedDocument`.
\r
360 * @param[in] pcSectionKey Either "previous" or "current". Must be NULL-terminated.
\r
361 * @param[out] ppcState Set to the first character in "state".
\r
362 * @param[out] pxStateLength Length of the "state" section.
\r
364 * @return pdTRUE if the "state" was found; pdFALSE otherwise.
\r
366 static BaseType_t prvGetUpdatedState( const char * pcUpdatedDocument,
\r
367 size_t xUpdatedDocumentLength,
\r
368 const char * pcSectionKey,
\r
369 const char ** ppcState,
\r
370 size_t * pxStateLength );
\r
372 /*-----------------------------------------------------------*/
\r
375 * @brief Parameters used to create the system task pool.
\r
377 static const IotTaskPoolInfo_t xTaskPoolParameters =
\r
379 /* Minimum number of threads in a task pool.
\r
380 * Note the slimmed down version of the task
\r
381 * pool used by this library does not auto-scale
\r
382 * the number of tasks in the pool so in this
\r
383 * case this sets the number of tasks in the
\r
387 /* Maximum number of threads in a task pool.
\r
388 * Note the slimmed down version of the task
\r
389 * pool used by this library does not auto-scale
\r
390 * the number of tasks in the pool so in this
\r
391 * case this parameter is just ignored. */
\r
394 /* Stack size for every task pool thread - in
\r
395 * bytes, hence multiplying by the number of bytes
\r
396 * in a word as configMINIMAL_STACK_SIZE is
\r
397 * specified in words. */
\r
398 configMINIMAL_STACK_SIZE * sizeof( portSTACK_TYPE ),
\r
399 /* Priority for every task pool thread. */
\r
403 /*-----------------------------------------------------------*/
\r
405 static const struct IotNetworkServerInfo xMQTTBrokerInfo =
\r
407 .pHostName = awsiotdemoprofileAWS_ENDPOINT,
\r
408 .port = awsiotdemoprofileAWS_MQTT_PORT
\r
411 static struct IotNetworkCredentials xNetworkSecurityCredentials =
\r
413 /* Optional TLS extensions. For this demo, they are disabled. */
\r
414 .pAlpnProtos = NULL,
\r
415 .maxFragmentLength = 0,
\r
417 /* SNI is enabled by default. */
\r
418 .disableSni = false,
\r
420 /* Provide the certificate for validating the server. Only required for
\r
421 demos using TLS. */
\r
422 .pRootCa = awsiotdemoprofileAWS_CERTIFICATE_PEM,
\r
423 .rootCaSize = sizeof( awsiotdemoprofileAWS_CERTIFICATE_PEM ),
\r
425 /* Strong mutual authentication to authenticate both the broker and
\r
427 .pClientCert = awsiotdemoprofileCLIENT_CERTIFICATE_PEM,
\r
428 .clientCertSize = sizeof( awsiotdemoprofileCLIENT_CERTIFICATE_PEM ),
\r
429 .pPrivateKey = awsiotdemoprofileCLIENT_PRIVATE_KEY_PEM,
\r
430 .privateKeySize = sizeof( awsiotdemoprofileCLIENT_PRIVATE_KEY_PEM )
\r
433 static IotMqttNetworkInfo_t xNetworkInfo =
\r
435 /* No connection to the MQTT broker has been established yet and we want to
\r
436 * establish a new connection. */
\r
437 .createNetworkConnection = true,
\r
438 .u.setup.pNetworkServerInfo = &( xMQTTBrokerInfo ),
\r
440 /* Set the TLS credentials for the new MQTT connection. This member is NULL
\r
441 * for the plain text MQTT demo. */
\r
442 .u.setup.pNetworkCredentialInfo = &xNetworkSecurityCredentials,
\r
444 /* Use FreeRTOS+TCP network interface. */
\r
445 .pNetworkInterface = IOT_NETWORK_INTERFACE_FREERTOS,
\r
447 /* Setup the callback which is called when the MQTT connection is
\r
448 * disconnected. The task handle is passed as the callback context which
\r
449 * is used by the callback to send a task notification to this task.*/
\r
450 .disconnectCallback.function = prvExample_OnDisconnect
\r
454 static const IotMqttConnectInfo_t xConnectInfo =
\r
456 /* Set this flag to true if connecting to the AWS IoT MQTT broker. */
\r
457 .awsIotMqttMode = true,
\r
459 /* Start with a clean session i.e. direct the MQTT broker to discard any
\r
460 * previous session data. Also, establishing a connection with clean session
\r
461 * will ensure that the broker does not store any data when this client
\r
462 * gets disconnected. */
\r
463 .cleanSession = true,
\r
465 /* Since we are starting with a clean session, there are no previous
\r
466 * subscriptions to be restored. */
\r
467 .pPreviousSubscriptions = NULL,
\r
468 .previousSubscriptionCount = 0,
\r
470 /* We do not want to publish Last Will and Testament (LWT) message if the
\r
471 * client gets disconnected. */
\r
474 /* Send an MQTT PING request every minute to keep the connection open if
\r
475 there is no other MQTT traffic. */
\r
476 .keepAliveSeconds = shadowexampleKEEP_ALIVE_SECONDS,
\r
478 /* The client identifier is used to uniquely identify this MQTT client to
\r
479 * the MQTT broker. In a production device the identifier can be something
\r
480 * unique, such as a device serial number. */
\r
481 .pClientIdentifier = awsiotdemoprofileCLIENT_IDENTIFIER,
\r
482 .clientIdentifierLength = ( uint16_t ) sizeof( awsiotdemoprofileCLIENT_IDENTIFIER ) - 1,
\r
484 /* This example does not authenticate the client and therefore username and
\r
485 * password fields are not used. */
\r
487 .userNameLength = 0,
\r
489 .passwordLength = 0
\r
491 /*-----------------------------------------------------------*/
\r
493 void vStartShadowDeviceOperationsDemo( void )
\r
495 TickType_t xShortDelay = ( TickType_t ) pdMS_TO_TICKS( ( TickType_t ) 500 );
\r
497 /* Wait a short time to allow receipt of the ARP replies. */
\r
498 vTaskDelay( xShortDelay );
\r
500 /* This example uses a single application task, which in turn is used to
\r
501 * connect, subscribe, publish, unsubscribe and disconnect from the MQTT
\r
503 xTaskCreate( prvShadowDemoTask, /* Function that implements the task. */
\r
504 "ShadowDemo", /* Text name for the task - only used for debugging. */
\r
505 democonfigDEMO_STACKSIZE,/* Size of stack (in words, not bytes) to allocate for the task. */
\r
506 NULL, /* Task parameter - not used in this case. */
\r
507 tskIDLE_PRIORITY, /* Task priority, must be between 0 and configMAX_PRIORITIES - 1. */
\r
508 NULL ); /* Used to pass out a handle to the created task - not used in this case. */
\r
510 /*-----------------------------------------------------------*/
\r
512 static void prvShadowDemoTask( void *pvParameters )
\r
514 uint32_t ulNotificationValue = 0;
\r
515 const TickType_t xNoDelay = ( TickType_t ) 0;
\r
517 /* Remove compiler warnings about unused parameters. */
\r
518 ( void ) pvParameters;
\r
520 /* One time initialization of the libraries used by this demo. */
\r
521 prvInitialiseLibraries();
\r
525 /* Don't expect any notifications to be pending yet. */
\r
526 configASSERT( ulTaskNotifyTake( pdTRUE, xNoDelay ) == 0 );
\r
528 /****************************** Connect. ******************************/
\r
530 /* Establish a connection to the AWS IoT MQTT broker. This example connects to
\r
531 * the MQTT broker as specified in awsiotdemoprofileAWS_ENDPOINT and
\r
532 * awsiotdemoprofileAWS_MQTT_PORT at the top of this file.
\r
534 configPRINTF( ( "Attempt to connect to %s\r\n", awsiotdemoprofileAWS_ENDPOINT ) );
\r
536 configPRINTF( ( "Connected to %s\r\n", awsiotdemoprofileAWS_ENDPOINT ) );
\r
538 /************************ Create a semaphore **************************/
\r
540 /* Creates a semaphore to synchronize between delta callback and
\r
543 configPRINTF( ( "Creating delta semaphore\r\n" ) );
\r
544 configASSERT( xSemaphoreCreateCountingStatic( 1, 0, &xDeltaSemaphore.xSemaphore ) != NULL );
\r
546 /************************ Set shadow callbacks ************************/
\r
548 /* Sets the updated callback and delta callback */
\r
549 configPRINTF( ( "Setting the updated callback and delta callback\r\n" ) );
\r
550 prvSetShadowCallbacks();
\r
552 /************************ Clear shadow document ***********************/
\r
554 /* Clears the Shadow document if it exists already */
\r
555 configPRINTF( ( "Clearing the Shadow document if it already exits\r\n" ) );
\r
556 prvClearShadowDocument();
\r
558 /*********************** Send Shadow updates **************************/
\r
560 /* Send Shadow updates for shadowexampleUPDATE_COUNT times.
\r
561 * For each Shadow update, it waits on xDeltaSemaphore. xDeltaSemaphore
\r
562 * will be posted by the delta callback.
\r
564 configPRINTF( ( "Sending Shadow updates\r\n" ) );
\r
565 prvSendShadowUpdates();
\r
567 /************************ Clear shadow document ***********************/
\r
569 /* Clears the Shadow document at the end of the demo */
\r
570 configPRINTF( ( "Clearing the Shadow document\r\n" ) );
\r
571 prvClearShadowDocument();
\r
573 /************** Clear callbacks and Disconnect MQTT. ******************/
\r
575 /* Clear updated callback and delta callback */
\r
576 configPRINTF( ( "Clearing the Shadow updated callback and delta callback\r\n" ) );
\r
577 prvClearShadowCallbacks();
\r
579 /* Disconnect MQTT gracefully. */
\r
580 prvMQTTDisconnect();
\r
581 configPRINTF( ( "Disconnected from %s\r\n\r\n", awsiotdemoprofileAWS_ENDPOINT ) );
\r
583 /* Wait for the disconnect operation to complete which is informed to us
\r
584 * by the disconnect callback (prvExample_OnDisconnect)by setting
\r
585 * the shadowexampleDISCONNECTED_BIT in this task's notification value.
\r
586 * Note that the bit is cleared in the task's notification value to
\r
587 * ensure that it is ready for the next run. */
\r
588 xTaskNotifyWait( 0UL, /* Don't clear any bits on entry. */
\r
589 shadowexampleDISCONNECTED_BIT, /* Clear bit on exit. */
\r
590 &( ulNotificationValue ), /* Obtain the notification value. */
\r
591 pdMS_TO_TICKS( shadowexampleMQTT_TIMEOUT_MS ) );
\r
592 configASSERT( ( ulNotificationValue & shadowexampleDISCONNECTED_BIT ) == shadowexampleDISCONNECTED_BIT );
\r
594 /* Destroy the delta semaphore*/
\r
595 vSemaphoreDelete( ( SemaphoreHandle_t ) &xDeltaSemaphore.xSemaphore );
\r
597 /* Clear the current reported shadow state to toggle the reported state. */
\r
598 lDevicePowerOnState = 0;
\r
600 /* Wait for some time between two iterations to ensure that we do not
\r
601 * bombard the broker. */
\r
602 configPRINTF( ( "prvShadowDemoTask() completed an iteration successfully. Total free heap is %u\r\n", xPortGetFreeHeapSize() ) );
\r
603 configPRINTF( ( "Demo completed successfully.\r\n" ) );
\r
604 configPRINTF( ( "Short delay before starting the next iteration... \r\n\r\n" ) );
\r
605 vTaskDelay( pdMS_TO_TICKS( shadowexampleLOOP_WAIT_PERIOD_MS ) );
\r
608 /*-----------------------------------------------------------*/
\r
610 static void prvExample_OnDisconnect( void * pvCallbackContext,
\r
611 IotMqttCallbackParam_t * pxCallbackParams )
\r
613 TaskHandle_t xDemoTaskHandle = ( TaskHandle_t ) pvCallbackContext;
\r
615 /* Ensure that we initiated the disconnect. */
\r
616 configASSERT( pxCallbackParams->u.disconnectReason == IOT_MQTT_DISCONNECT_CALLED );
\r
618 /* Inform the demo task about the disconnect. */
\r
619 xTaskNotify( xDemoTaskHandle,
\r
620 shadowexampleDISCONNECTED_BIT,
\r
621 eSetBits /* Set the shadowexampleDISCONNECTED_BIT in the demo task's notification value. */
\r
624 /*-----------------------------------------------------------*/
\r
626 static void prvMQTTConnect( void )
\r
628 IotMqttError_t xResult;
\r
630 /* Set the context to pass into the disconnect callback function. */
\r
631 xNetworkInfo.disconnectCallback.pCallbackContext = ( void * ) xTaskGetCurrentTaskHandle();
\r
633 /* Establish the connection to the MQTT broker - It is a blocking call and
\r
634 * will return only when connection is complete or a timeout occurs. */
\r
635 xResult = IotMqtt_Connect( &( xNetworkInfo ),
\r
637 shadowexampleMQTT_TIMEOUT_MS,
\r
638 &( xMQTTConnection ) );
\r
639 configASSERT( xResult == IOT_MQTT_SUCCESS );
\r
641 /*-----------------------------------------------------------*/
\r
643 static void prvMQTTDisconnect( void )
\r
645 /* Send a MQTT DISCONNECT packet to the MQTT broker to do a graceful
\r
647 IotMqtt_Disconnect( xMQTTConnection,
\r
648 0 /* flags - 0 means a graceful disconnect by sending MQTT DISCONNECT. */
\r
651 /*-----------------------------------------------------------*/
\r
653 static void prvSetShadowCallbacks( void )
\r
655 AwsIotShadowError_t xCallbackStatus = AWS_IOT_SHADOW_STATUS_PENDING;
\r
656 AwsIotShadowCallbackInfo_t xDeltaCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER,
\r
657 xUpdatedCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
\r
659 /* Set the functions for callbacks. */
\r
660 xDeltaCallback.pCallbackContext = &xDeltaSemaphore;
\r
661 xDeltaCallback.function = prvShadowDeltaCallback;
\r
662 xUpdatedCallback.function = prvShadowUpdatedCallback;
\r
664 /************************ Set delta callbacks ****************************/
\r
666 /* Set the delta callback, which notifies of different desired and reported
\r
667 * Shadow states. */
\r
668 xCallbackStatus = AwsIotShadow_SetDeltaCallback( xMQTTConnection,
\r
669 awsiotdemoprofileCLIENT_IDENTIFIER,
\r
670 shadowexampleCLIENT_IDENTIFIER_LENGTH,
\r
671 0, &xDeltaCallback );
\r
672 configASSERT( xCallbackStatus == AWS_IOT_SHADOW_SUCCESS );
\r
674 /************************ Set updated callbacks **************************/
\r
676 /* Set the updated callback, which notifies when a Shadow document is
\r
678 xCallbackStatus = AwsIotShadow_SetUpdatedCallback( xMQTTConnection,
\r
679 awsiotdemoprofileCLIENT_IDENTIFIER,
\r
680 shadowexampleCLIENT_IDENTIFIER_LENGTH,
\r
681 0, &xUpdatedCallback );
\r
683 configASSERT( xCallbackStatus == AWS_IOT_SHADOW_SUCCESS );
\r
685 /*-----------------------------------------------------------*/
\r
687 static void prvClearShadowCallbacks( void )
\r
689 AwsIotShadowError_t xCallbackStatus = AWS_IOT_SHADOW_STATUS_PENDING;
\r
691 /************************ Clear delta callbacks **************************/
\r
692 xCallbackStatus = AwsIotShadow_SetDeltaCallback( xMQTTConnection,
\r
693 awsiotdemoprofileCLIENT_IDENTIFIER,
\r
694 shadowexampleCLIENT_IDENTIFIER_LENGTH,
\r
696 configASSERT( xCallbackStatus == AWS_IOT_SHADOW_SUCCESS );
\r
698 /************************ Clear updated callbacks ************************/
\r
699 xCallbackStatus = AwsIotShadow_SetUpdatedCallback( xMQTTConnection,
\r
700 awsiotdemoprofileCLIENT_IDENTIFIER,
\r
701 shadowexampleCLIENT_IDENTIFIER_LENGTH,
\r
703 configASSERT( xCallbackStatus == AWS_IOT_SHADOW_SUCCESS );
\r
705 /*-----------------------------------------------------------*/
\r
707 static void prvShadowDeltaCallback( void * pCallbackContext,
\r
708 AwsIotShadowCallbackParam_t * pxCallbackParam )
\r
710 BaseType_t xDeltaFound = pdFALSE;
\r
711 const char * pcDelta = NULL;
\r
712 size_t xDeltaLength = 0;
\r
713 IotSemaphore_t * pxDeltaSemaphore = pCallbackContext;
\r
714 uint32_t ulUpdateDocumentLength = 0;
\r
715 AwsIotShadowError_t xShadowStatus = AWS_IOT_SHADOW_STATUS_PENDING;
\r
716 AwsIotShadowDocumentInfo_t xUpdateDocument = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
\r
717 uint8_t ucNewState = 0;
\r
719 configASSERT( pxDeltaSemaphore != NULL );
\r
720 configASSERT( pxCallbackParam != NULL );
\r
722 /* A buffer containing the update document. It has static duration to prevent
\r
723 * it from being placed on the call stack.This is only safe because there
\r
724 * is only one task in the task pool so this function cannot be called from
\r
725 * two tasks simultaneously. */
\r
726 static char cUpdateDocument[ shadowexampleREPORTED_JSON_SIZE + 1 ] = { 0 };
\r
728 /****************** Get delta state from Shadow document *****************/
\r
729 /* Check if there is a different "powerOn" state in the Shadow. */
\r
730 xDeltaFound = prvGetDelta( pxCallbackParam->u.callback.pDocument,
\r
731 pxCallbackParam->u.callback.documentLength,
\r
736 configASSERT( xDeltaFound == pdTRUE );
\r
738 /* Change the current state based on the value in the delta document. */
\r
739 if( *pcDelta == '0' )
\r
743 else if( *pcDelta == '1' )
\r
749 configPRINTF( ( "Unknown powerOn state parsed from delta document.\r\n" ) );
\r
751 /* Set new state to current state to ignore the delta document. */
\r
752 ucNewState = lDevicePowerOnState;
\r
755 if( ucNewState != lDevicePowerOnState )
\r
757 /* Toggle state. */
\r
758 configPRINTF( ( "%.*s changing state from %d to %d.\r\n",
\r
759 pxCallbackParam->thingNameLength,
\r
760 pxCallbackParam->pThingName,
\r
761 lDevicePowerOnState,
\r
764 lDevicePowerOnState = ucNewState;
\r
766 /* Set the common members to report the new state. */
\r
767 xUpdateDocument.pThingName = pxCallbackParam->pThingName;
\r
768 xUpdateDocument.thingNameLength = pxCallbackParam->thingNameLength;
\r
769 xUpdateDocument.u.update.pUpdateDocument = cUpdateDocument;
\r
770 xUpdateDocument.u.update.updateDocumentLength = shadowexampleREPORTED_JSON_SIZE;
\r
772 /* Generate a Shadow document for the reported state. To keep the client
\r
773 * token within 6 characters, it is modded by 1000000. */
\r
774 ulUpdateDocumentLength = snprintf( cUpdateDocument,
\r
775 shadowexampleREPORTED_JSON_SIZE + 1,
\r
776 shadowexampleREPORTED_JSON,
\r
777 ( int ) lDevicePowerOnState,
\r
778 ( long unsigned ) ( IotClock_GetTimeMs() % 1000000 ) );
\r
780 /* Check if the reported state document is generated for Shadow update*/
\r
781 configASSERT( ( size_t ) ulUpdateDocumentLength == shadowexampleREPORTED_JSON_SIZE );
\r
783 /* Send the Shadow update. Its result is not checked by waiting for the
\r
784 * callback, as the Shadow updated callback will report if the Shadow
\r
785 * was successfully updated. As the Shadow is constantly updated
\r
786 * in this demo, the "Keep Subscriptions" flag is passed to this
\r
788 xShadowStatus = AwsIotShadow_UpdateAsync( pxCallbackParam->mqttConnection,
\r
790 AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS,
\r
794 configASSERT( xShadowStatus == AWS_IOT_SHADOW_STATUS_PENDING );
\r
795 configPRINTF( ( "%.*s sent new state report: %.*s\r\n",
\r
796 pxCallbackParam->thingNameLength,
\r
797 pxCallbackParam->pThingName,
\r
798 shadowexampleREPORTED_JSON_SIZE,
\r
799 cUpdateDocument ) );
\r
801 /* Post to the delta semaphore to unblock the thread sending Shadow updates. */
\r
802 xSemaphoreGive( ( SemaphoreHandle_t ) &pxDeltaSemaphore->xSemaphore );
\r
805 /*-----------------------------------------------------------*/
\r
807 static void prvShadowUpdatedCallback( void * pCallbackContext,
\r
808 AwsIotShadowCallbackParam_t * pxCallbackParam )
\r
810 BaseType_t xPreviousFound = pdFALSE, xCurrentFound = pdFALSE;
\r
811 const char * pcPrevious = NULL, * pcCurrent = NULL;
\r
812 size_t xPreviousLength = 0, xCurrentLength = 0;
\r
814 /* Silence warnings about unused parameters. */
\r
815 ( void ) pCallbackContext;
\r
817 configASSERT( pxCallbackParam != NULL );
\r
819 /****************** Get previous state from Shadow document **************/
\r
820 /* Find the previous Shadow document. */
\r
821 xPreviousFound = prvGetUpdatedState( pxCallbackParam->u.callback.pDocument,
\r
822 pxCallbackParam->u.callback.documentLength,
\r
825 &xPreviousLength );
\r
827 /****************** Get current state from Shadow document **************/
\r
828 /* Find the current Shadow document. */
\r
829 xCurrentFound = prvGetUpdatedState( pxCallbackParam->u.callback.pDocument,
\r
830 pxCallbackParam->u.callback.documentLength,
\r
835 configASSERT( ( xPreviousFound == pdTRUE ) || ( xCurrentFound == pdTRUE ) );
\r
837 /* Log the previous and current states. */
\r
838 configPRINTF( ( "Shadow was updated!\r\n"
\r
839 "Previous: {\"state\":%.*s}\r\n"
\r
840 "Current: {\"state\":%.*s}\r\n",
\r
846 /*-----------------------------------------------------------*/
\r
848 static BaseType_t prvGetDelta( const char * pcDeltaDocument,
\r
849 size_t xDeltaDocumentLength,
\r
850 const char * pcDeltaKey,
\r
851 const char ** pcDelta,
\r
852 size_t * pcDeltaLength )
\r
854 BaseType_t xStateFound = pdFALSE, xDeltaFound = pdFALSE;
\r
855 const size_t xDeltaKeyLength = strlen( pcDeltaKey );
\r
856 const char * pcState = NULL;
\r
857 size_t xStateLength = 0;
\r
859 configASSERT( pcDeltaDocument != NULL );
\r
860 configASSERT( pcDeltaKey != NULL );
\r
861 /****************** Get state from Shadow document ***********************/
\r
863 /* Note: This parser used is specific for parsing AWS IoT document received
\r
864 * through a mutually authenticated connection. This parser will not check
\r
865 * for the correctness of the document as it is designed for low memory
\r
866 * footprint rather than checking for correctness of the document. This
\r
867 * parser is not meant to be used as a general purpose JSON parser.
\r
869 xStateFound = ( BaseType_t ) AwsIotDocParser_FindValue(
\r
871 xDeltaDocumentLength,
\r
877 configASSERT( xStateFound == pdTRUE );
\r
879 /********** Get delta key from state section of Shadow document **********/
\r
881 /* Note: This parser used is specific for parsing AWS IoT document received
\r
882 * through a mutually authenticated connection. This parser will not check
\r
883 * for the correctness of the document as it is designed for low memory
\r
884 * footprint rather than checking for correctness of the document. This
\r
885 * parser is not meant to be used as a general purpose JSON parser.
\r
887 xDeltaFound = ( BaseType_t ) AwsIotDocParser_FindValue(
\r
895 return xDeltaFound;
\r
897 /*-----------------------------------------------------------*/
\r
899 static BaseType_t prvGetUpdatedState( const char * pcUpdatedDocument,
\r
900 size_t xUpdatedDocumentLength,
\r
901 const char * pcSectionKey,
\r
902 const char ** ppcState,
\r
903 size_t * ppcStateLength )
\r
905 BaseType_t xSectionFound = pdFALSE, xStateFound = pdFALSE;
\r
906 const size_t xSectionKeyLength = strlen( pcSectionKey );
\r
907 const char * pcSection = NULL;
\r
908 size_t xSectionLength = 0;
\r
910 configASSERT( pcUpdatedDocument != NULL );
\r
911 configASSERT( pcSectionKey != NULL );
\r
913 /*********** Find the given section in the updated document. *************/
\r
915 /* Note: This parser used is specific for parsing AWS IoT document received
\r
916 * through a mutually authenticated connection. This parser will not check
\r
917 * for the correctness of the document as it is designed for low memory
\r
918 * footprint rather than checking for correctness of the document. This
\r
919 * parser is not meant to be used as a general purpose JSON parser.
\r
921 xSectionFound = ( BaseType_t ) AwsIotDocParser_FindValue(
\r
923 xUpdatedDocumentLength,
\r
929 configASSERT( xSectionFound == pdTRUE );
\r
931 /*********** Find the state key within the section found *****************/
\r
933 /* Find the "state" key within the "previous" or "current" section.
\r
935 * Note: This parser used is specific for parsing AWS IoT document received
\r
936 * through a mutually authenticated connection. This parser will not check
\r
937 * for the correctness of the document as it is designed for low memory
\r
938 * footprint rather than checking for correctness of the document. This
\r
939 * parser is not meant to be used as a general purpose JSON parser.
\r
941 xStateFound = ( BaseType_t ) AwsIotDocParser_FindValue(
\r
949 return xStateFound;
\r
951 /*-----------------------------------------------------------*/
\r
953 static void prvClearShadowDocument( void )
\r
955 AwsIotShadowError_t xDeleteStatus = AWS_IOT_SHADOW_STATUS_PENDING;
\r
957 /************************* Delete Shadow document ************************/
\r
958 xDeleteStatus = AwsIotShadow_DeleteSync( xMQTTConnection,
\r
959 awsiotdemoprofileCLIENT_IDENTIFIER,
\r
960 shadowexampleCLIENT_IDENTIFIER_LENGTH,
\r
961 0, shadowexampleMQTT_TIMEOUT_MS );
\r
962 configASSERT( ( xDeleteStatus == AWS_IOT_SHADOW_SUCCESS ) || ( xDeleteStatus == AWS_IOT_SHADOW_NOT_FOUND ) );
\r
964 configPRINTF( ( "Successfully cleared Shadow of %.*s.\r\n",
\r
965 shadowexampleCLIENT_IDENTIFIER_LENGTH,
\r
966 awsiotdemoprofileCLIENT_IDENTIFIER ) );
\r
968 /*-----------------------------------------------------------*/
\r
970 static void prvSendShadowUpdates( void )
\r
972 int32_t lIndex = 0, lDesiredState = 0, lStatus = 0;
\r
973 AwsIotShadowError_t xShadowStatus = AWS_IOT_SHADOW_STATUS_PENDING;
\r
974 AwsIotShadowDocumentInfo_t xUpdateDocument = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
\r
976 /* A buffer containing the update document. It has static duration to prevent
\r
977 * it from being placed on the call stack. */
\r
978 static char cUpdateDocument[ shadowexampleDESIRED_JSON_SIZE + 1 ] = { 0 };
\r
980 /********** Set the common members of Shadow update document *************/
\r
981 xUpdateDocument.pThingName = awsiotdemoprofileCLIENT_IDENTIFIER;
\r
982 xUpdateDocument.thingNameLength = shadowexampleCLIENT_IDENTIFIER_LENGTH;
\r
983 xUpdateDocument.u.update.pUpdateDocument = cUpdateDocument;
\r
984 xUpdateDocument.u.update.updateDocumentLength = shadowexampleDESIRED_JSON_SIZE;
\r
986 /*************** Publish Shadow updates at a set period. *****************/
\r
987 for( lIndex = 1; lIndex <= shadowexampleUPDATE_COUNT; lIndex++ )
\r
989 /* Toggle the desired state. */
\r
990 lDesiredState = !( lDesiredState );
\r
992 /* Generate a Shadow desired state document, using a timestamp for the client
\r
993 * token. To keep the client token within 6 characters, it is modded by 1000000. */
\r
994 lStatus = snprintf( cUpdateDocument,
\r
995 shadowexampleDESIRED_JSON_SIZE + 1,
\r
996 shadowexampleDESIRED_JSON,
\r
997 ( int ) lDesiredState,
\r
998 ( long unsigned ) ( IotClock_GetTimeMs() % 1000000 ) );
\r
1000 /* Check for errors from snprintf. The expected value is the length of
\r
1001 * the desired JSON document less the format specifier for the state. */
\r
1002 configASSERT( lStatus == shadowexampleDESIRED_JSON_SIZE );
\r
1004 configPRINTF( ( "Sending Shadow update %d of %d: %s\r\n",
\r
1006 shadowexampleUPDATE_COUNT,
\r
1007 cUpdateDocument ) );
\r
1009 /* Send the Shadow update. Because the Shadow is constantly updated in
\r
1010 * this demo, the "Keep Subscriptions" flag is passed to this function.
\r
1011 * Note that this flag only needs to be passed on the first call, but
\r
1012 * passing it for subsequent calls is fine.
\r
1014 xShadowStatus = AwsIotShadow_UpdateSync( xMQTTConnection,
\r
1016 AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS,
\r
1017 shadowexampleMQTT_TIMEOUT_MS );
\r
1019 configASSERT( xShadowStatus == AWS_IOT_SHADOW_SUCCESS );
\r
1021 configPRINTF( ( "Successfully sent Shadow update %d of %d.\r\n",
\r
1023 shadowexampleUPDATE_COUNT ) );
\r
1025 /* Wait for the delta callback to change its state before continuing. */
\r
1026 configASSERT( xSemaphoreTake( ( SemaphoreHandle_t ) &xDeltaSemaphore.xSemaphore,
\r
1027 pdMS_TO_TICKS( shadowexampleWAIT_PERIOD_FOR_DELTA_MS ) ) == pdTRUE );
\r
1029 IotClock_SleepMs( shadowexampleUPDATE_PERIOD_MS );
\r
1032 /* Remove persistent subscriptions. In the AwsIotShadow_UpdateSync call, we have used the */
\r
1033 xShadowStatus = AwsIotShadow_RemovePersistentSubscriptions( xMQTTConnection,
\r
1034 awsiotdemoprofileCLIENT_IDENTIFIER,
\r
1035 shadowexampleCLIENT_IDENTIFIER_LENGTH,
\r
1036 AWS_IOT_SHADOW_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS );
\r
1038 configASSERT( xShadowStatus == AWS_IOT_SHADOW_SUCCESS );
\r
1040 /*-----------------------------------------------------------*/
\r
1042 static void prvInitialiseLibraries( void )
\r
1044 IotTaskPoolError_t xTaskPoolResult;
\r
1045 IotMqttError_t xResult;
\r
1046 IotNetworkError_t xNetworkResult;
\r
1048 /* The MQTT library needs a task pool, so create the system task pool. */
\r
1049 xTaskPoolResult = IotTaskPool_CreateSystemTaskPool( &( xTaskPoolParameters ) );
\r
1050 configASSERT( xTaskPoolResult == IOT_TASKPOOL_SUCCESS );
\r
1052 /* Initialize the network stack abstraction for FreeRTOS. */
\r
1053 xNetworkResult = IotNetworkFreeRTOS_Init();
\r
1054 configASSERT( xNetworkResult == IOT_NETWORK_SUCCESS );
\r
1056 /* MQTT library must be initialized before it can be used. This is just one
\r
1057 * time initialization. */
\r
1058 xResult = IotMqtt_Init();
\r
1059 configASSERT( xResult == IOT_MQTT_SUCCESS );
\r
1061 /* Initialize Shadow library*/
\r
1062 xResult = AwsIotShadow_Init( shadowexampleUSE_DEFAULT_MQTT_TIMEOUT );
\r
1063 configASSERT( xResult == AWS_IOT_SHADOW_SUCCESS );
\r
1065 /*-----------------------------------------------------------*/
\r