]> git.sur5r.net Git - freertos/blobdiff - FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/jobs/jobs_notify_next/DemoTasks/JobsNotifyNextExamples.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Demo / FreeRTOS_IoT_Libraries / jobs / jobs_notify_next / DemoTasks / JobsNotifyNextExamples.c
diff --git a/FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/jobs/jobs_notify_next/DemoTasks/JobsNotifyNextExamples.c b/FreeRTOS-Labs/Demo/FreeRTOS_IoT_Libraries/jobs/jobs_notify_next/DemoTasks/JobsNotifyNextExamples.c
new file mode 100644 (file)
index 0000000..89d36a4
--- /dev/null
@@ -0,0 +1,1079 @@
+/*\r
+ * FreeRTOS Kernel V10.2.1\r
+ * Copyright (C) 2019 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://www.FreeRTOS.org\r
+ * http://aws.amazon.com/freertos\r
+ *\r
+ * 1 tab == 4 spaces!\r
+ */\r
+\r
+/* This demo executes Jobs obtained from AWS IoT. An AWS IoT Job is used to define\r
+ * a set of remote operations that are sent to and executed on one or more devices\r
+ * connected to AWS IoT. Please refer to AWS documentation for more information\r
+ * about AWS IoT Jobs.\r
+ * https://docs.aws.amazon.com/iot/latest/developerguide/iot-jobs.html\r
+ *\r
+ * This demo creates a single application task that sets a callback for the\r
+ * jobs/notify-next topic and executes Jobs created from the AWS IoT console or AWS\r
+ * CLI. Please refer to AWS CLI documentation for more information in creating a\r
+ * Job document.\r
+ * https://docs.aws.amazon.com/cli/latest/reference/iot/create-job.html\r
+ *\r
+ * This demo expects Job documents to have an "action" JSON key. Actions can\r
+ * be one "print", "publish", or "exit".\r
+ * Print Jobs log a message to the local console, and must contain a "message",\r
+ * e.g. { "action": "print", "message": "Hello World!" }.\r
+ * Publish Jobs publish a message to an MQTT Topic. The Job document must\r
+ * contain a "message" and "topic" to publish to, e.g.\r
+ * { "action": "publish", "topic": "demo/jobs", "message": "Hello World!" }.\r
+ * The exit Job exits the demo. Sending { "action": "exit" } will end the program.\r
+ */\r
+\r
+/* Standard includes. */\r
+#include <string.h>\r
+#include <stdio.h>\r
+\r
+/* Kernel includes. */\r
+#include "FreeRTOS.h"\r
+#include "task.h"\r
+\r
+/* FreeRTOS+TCP includes. */\r
+#include "FreeRTOS_IP.h"\r
+\r
+/* IoT SDK includes. */\r
+#include "aws_iot_jobs.h"\r
+#include "aws_iot_demo_profile.h"\r
+#include "iot_mqtt.h"\r
+#include "iot_taskpool_freertos.h"\r
+#include "aws_iot_doc_parser.h"\r
+#include "platform/iot_clock.h"\r
+#include "platform/iot_threads.h"\r
+#include "platform/iot_network_freertos.h"\r
+\r
+#include "atomic.h"\r
+\r
+/* Preprocessor check iot configuration. */\r
+#include "aws_iot_setup_check.h"\r
+\r
+/* Demo specific includes. */\r
+#include "demo_config.h"\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief The keep-alive interval used for this example.\r
+ *\r
+ * An MQTT ping request will be sent periodically at this interval.\r
+ *\r
+ * @note: This value is set to zero to disable MQTT\r
+ * keep alive for the Windows simulator project.\r
+ * The FreeRTOS kernel does not accurately calculate time for the Windows\r
+ * Simulator. Therefore, MQTT PING Request messages may be sent\r
+ * at an incorrect time interval to the broker. If the broker does\r
+ * not receive a ping request within 1.5x the time sent in a\r
+ * connection request, the broker may close the connection.\r
+ * To enable the keep alive feature, set this value\r
+ * to the desired interval in seconds.\r
+ */\r
+#define jobsexampleKEEP_ALIVE_SECONDS          ( 0 )\r
+\r
+/**\r
+ * @brief The timeout for MQTT operations in this example.\r
+ */\r
+#define jobsexampleMQTT_TIMEOUT_MS             ( 5000 )\r
+\r
+/**\r
+ * @brief Use default timeout when calling AwsIotJobs_Init.\r
+ */\r
+#define jobsexampleUSE_DEFAULT_MQTT_TIMEOUT    ( 0 )\r
+\r
+/**\r
+ * @brief The bit which is set in the demo task's notification value from the\r
+ * disconnect callback to inform the demo task about the MQTT disconnect.\r
+ */\r
+#define jobsexampleDISCONNECTED_BIT            ( 1UL << 0UL )\r
+\r
+/**\r
+ * @brief The bit which is set in the demo task's notification value from the\r
+ * operation complete callback to inform the demo task to exit.\r
+ */\r
+#define jobsexampleEXIT_BIT                    ( 1UL << 1UL )\r
+\r
+/**\r
+ * @brief Length of the client identifier for this demo.\r
+ */\r
+#define jobsexampleCLIENT_IDENTIFIER_LENGTH    ( sizeof( awsiotdemoprofileCLIENT_IDENTIFIER ) - 1 )\r
+\r
+/**\r
+ * @brief The JSON key of the Job ID.\r
+ *\r
+ * Job documents are in JSON documents received from the AWS IoT Jobs service.\r
+ * All such JSON documents will contain this key, whose value represents the unique\r
+ * identifier of a Job.\r
+ */\r
+#define jobsexampleID_KEY                      "jobId"\r
+\r
+/**\r
+ * @brief The length of #jobsexampleID_KEY.\r
+ */\r
+#define jobsexampleID_KEY_LENGTH               ( sizeof( jobsexampleID_KEY ) - 1 )\r
+\r
+/**\r
+ * @brief The JSON key of the Job document.\r
+ *\r
+ * Job documents are in JSON documents received from the AWS IoT Jobs service.\r
+ * All such JSON documents will contain this key, whose value is an application-specific\r
+ * Job document.\r
+ */\r
+#define jobsexampleDOC_KEY                     "jobDocument"\r
+\r
+/**\r
+ * @brief The length of #jobsexampleDOC_KEY.\r
+ */\r
+#define jobsexampleDOC_KEY_LENGTH              ( sizeof( jobsexampleDOC_KEY ) - 1 )\r
+\r
+/**\r
+ * @brief The JSON key whose value represents the action this demo should take.\r
+ *\r
+ * This demo program expects this key to be in the Job document. It is a key\r
+ * specific to this demo.\r
+ */\r
+#define jobsexampleACTION_KEY                  "action"\r
+\r
+/**\r
+ * @brief The length of #jobsexampleACTION_KEY.\r
+ */\r
+#define jobsexampleACTION_KEY_LENGTH           ( sizeof( jobsexampleACTION_KEY ) - 1 )\r
+\r
+/**\r
+ * @brief A message associated with the Job action.\r
+ *\r
+ * This demo program expects this key to be in the Job document if the "action"\r
+ * is either "publish" or "print". It represents the message that should be\r
+ * published or printed, respectively.\r
+ */\r
+#define jobsexampleMESSAGE_KEY                 "message"\r
+\r
+/**\r
+ * @brief The length of #jobsexampleMESSAGE_KEY.\r
+ */\r
+#define jobsexampleMESSAGE_KEY_LENGTH          ( sizeof( jobsexampleMESSAGE_KEY ) - 1 )\r
+\r
+/**\r
+ * @brief An MQTT topic associated with the Job "publish" action.\r
+ *\r
+ * This demo program expects this key to be in the Job document if the "action"\r
+ * is "publish". It represents the MQTT topic on which the message should be\r
+ * published.\r
+ */\r
+#define jobsexampleTOPIC_KEY                   "topic"\r
+\r
+/**\r
+ * @brief The length of #jobsexampleTOPIC_KEY.\r
+ */\r
+#define jobsexampleTOPIC_KEY_LENGTH            ( sizeof( jobsexampleTOPIC_KEY ) - 1 )\r
+\r
+/**\r
+ * @brief The minimum length of a string in a JSON Job document.\r
+ *\r
+ * At the very least the Job ID must have the quotes that identify it as a JSON\r
+ * string and 1 character for the string itself (the string must not be empty).\r
+ */\r
+#define jobsexampleJSON_STRING_MIN_LENGTH      ( ( size_t ) 3 )\r
+\r
+/**\r
+ * @brief The maximum length of a Job ID.\r
+ *\r
+ * This limit is defined by AWS service limits. See the following page for more\r
+ * information.\r
+ *\r
+ * https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html#job-limits\r
+ */\r
+#define jobsexampleID_MAX_LENGTH               ( ( size_t ) 64 )\r
+\r
+/**\r
+ * @brief A value passed as context to #prvOperationCompleteCallback to specify that\r
+ * it should notify the demo task of an exit request.\r
+ */\r
+#define jobsexampleSHOULD_EXIT                 ( ( void * ) ( ( intptr_t ) 1 ) )\r
+\r
+/**\r
+ * @brief Time to wait before exiting demo.\r
+ *\r
+ * The milliseconds to wait before exiting. This is because the MQTT Broker\r
+ * will disconnect us if we are idle too long, and we have disabled keep alive.\r
+ */\r
+#define jobsexampleMS_BEFORE_EXIT              ( 10 * 60 * 1000 )\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief Currently supported actions that a Job document can specify.\r
+ */\r
+typedef enum _jobAction\r
+{\r
+       JOB_ACTION_PRINT,   /**< Print a message. */\r
+       JOB_ACTION_PUBLISH, /**< Publish a message to an MQTT topic. */\r
+       JOB_ACTION_EXIT,    /**< Exit the demo. */\r
+       JOB_ACTION_UNKNOWN  /**< Unknown action. */\r
+} _jobAction_t;\r
+\r
+/**\r
+ * @brief The task used to demonstrate Jobs.\r
+ *\r
+ * @param[in] pvParameters Parameters as passed at the time of task creation. Not\r
+ * used in this example.\r
+ */\r
+static void prvJobsDemoTask( void * pvParameters );\r
+\r
+/**\r
+ * @brief The callback invoked by the MQTT library when the MQTT connection gets\r
+ * disconnected.\r
+ *\r
+ * @param[in] pvCallbackContext Callback context as provided at the time of\r
+ * connect.\r
+ * @param[in] pxCallbackParams Contains the reason why the MQTT connection was\r
+ * disconnected.\r
+ */\r
+static void prvExample_OnDisconnect( void * pvCallbackContext,\r
+                                                                        IotMqttCallbackParam_t * pxCallbackParams );\r
+\r
+/**\r
+ * @brief Connects to the MQTT broker as specified in awsiotdemoprofileAWS_ENDPOINT\r
+ * and awsiotdemoprofileAWS_MQTT_PORT.\r
+ */\r
+static void prvMQTTConnect( void );\r
+\r
+/**\r
+ * @brief Disconnects from the MQTT broker gracefully by sending an MQTT\r
+ * DISCONNECT message.\r
+ */\r
+static void prvMQTTDisconnect( void );\r
+\r
+/**\r
+ * @brief Set callback for publishes to the jobs/notify-next topic.\r
+ */\r
+static void prvSetNotifyNextCallback( void );\r
+\r
+/**\r
+ * @brief Converts a string in a Job document to a #_jobAction_t.\r
+ *\r
+ * @param[in] pcAction The Job action as a string.\r
+ * @param[in] xActionLength The length of `pcAction`.\r
+ *\r
+ * @return A #_jobAction_t equivalent to the given string.\r
+ */\r
+static _jobAction_t prvGetAction( const char * pcAction,\r
+                                                                 size_t xActionLength );\r
+\r
+/**\r
+ * @brief Extracts a JSON string from the Job document.\r
+ *\r
+ * @param[in] pcJsonDoc The JSON document to search.\r
+ * @param[in] xJsonDocLength Length of `pcJsonDoc`.\r
+ * @param[in] pcKey The JSON key to search for.\r
+ * @param[in] xKeyLength Length of `pcKey`.\r
+ * @param[out] ppcValue The extracted JSON value.\r
+ * @param[out] pxValueLength Length of ppcValue.\r
+ *\r
+ * @return `pdTRUE` if the key was found and the value is valid; `pdFALSE` otherwise.\r
+ */\r
+static BaseType_t prvGetJsonString( const char * pcJsonDoc,\r
+                                                                       size_t xJsonDocLength,\r
+                                                                       const char * pcKey,\r
+                                                                       size_t xKeyLength,\r
+                                                                       const char ** ppcValue,\r
+                                                                       size_t * pxValueLength );\r
+\r
+/**\r
+ * @brief Job operation completion callback. This function is invoked when an\r
+ * asynchronous Job operation finishes.\r
+ *\r
+ * @param[in] pvCallbackContext Set to a non-NULL value to exit the demo.\r
+ * @param[in] pxCallbackParam Information on the Job operation that completed.\r
+ */\r
+static void prvOperationCompleteCallback( void * pvCallbackContext,\r
+                                                                                 AwsIotJobsCallbackParam_t * pxCallbackParam );\r
+\r
+\r
+/**\r
+ * @brief Process an action with a message, such as "print" or "publish".\r
+ *\r
+ * @param[in] xMqttConnection The MQTT connection to use if the action is "publish".\r
+ * @param[in] xAction Either #JOB_ACTION_PRINT or #JOB_ACTION_PUBLISH.\r
+ * @param[in] pcJobDoc A pointer to the Job document.\r
+ * @param[in] xJobDocLength The length of the Job document.\r
+ *\r
+ * @return #AWS_IOT_JOB_STATE_SUCCEEDED on success; #AWS_IOT_JOB_STATE_FAILED otherwise.\r
+ */\r
+static AwsIotJobState_t prvProcessMessage( IotMqttConnection_t xMqttConnection,\r
+                                                                                  _jobAction_t xAction,\r
+                                                                                  const char * pcJobDoc,\r
+                                                                                  size_t xJobDocLength );\r
+\r
+/**\r
+ * @brief Process a Job received from the Notify Next callback.\r
+ *\r
+ * @param[in] pxJobInfo The parameter to the Notify Next callback that contains\r
+ * information about the received Job.\r
+ * @param[in] pcJobId A pointer to the Job ID.\r
+ * @param[in] xJobIdLength The length of the Job ID.\r
+ * @param[in] pcJobDoc A pointer to the Job document.\r
+ * @param[in] xJobDocLength The length of the Job document.\r
+ */\r
+static void prvProcessJob( const AwsIotJobsCallbackParam_t * pxJobInfo,\r
+                                                  const char * pcJobId,\r
+                                                  size_t xJobIdLength,\r
+                                                  const char * pcJobDoc,\r
+                                                  size_t xJobDocLength );\r
+\r
+/**\r
+ * @brief Jobs Notify Next callback. This function is invoked when a new Job is\r
+ * received from the Jobs service.\r
+ *\r
+ * @param[in] pCallbackContext Ignored.\r
+ * @param[in] pxCallbackInfo Contains the received Job.\r
+ */\r
+static void prvJobsCallback( void * pCallbackContext,\r
+                                                        AwsIotJobsCallbackParam_t * pxCallbackInfo );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/**\r
+ * @brief The MQTT connection handle used in this example.\r
+ */\r
+static IotMqttConnection_t xMQTTConnection = IOT_MQTT_CONNECTION_INITIALIZER;\r
+\r
+/*\r
+ * @brief The main task handle in this demo.\r
+ */\r
+static TaskHandle_t xMainTaskHandle;\r
+\r
+/**\r
+ * @brief Parameters used to create the system task pool.\r
+ */\r
+static const IotTaskPoolInfo_t xTaskPoolParameters =\r
+{\r
+       /* Minimum number of threads in a task pool.\r
+        * Note the slimmed down version of the task\r
+        * pool used by this library does not auto-scale\r
+        * the number of tasks in the pool so in this\r
+        * case this sets the number of tasks in the\r
+        * pool. */\r
+       1,\r
+\r
+       /* Maximum number of threads in a task pool.\r
+        * Note the slimmed down version of the task\r
+        * pool used by this library does not auto-scale\r
+        * the number of tasks in the pool so in this\r
+        * case this parameter is just ignored. */\r
+       1,\r
+\r
+       /* Stack size for every task pool thread - in\r
+        * bytes, hence multiplying by the number of bytes\r
+        * in a word as configMINIMAL_STACK_SIZE is\r
+        * specified in words. */\r
+       configMINIMAL_STACK_SIZE * sizeof( portSTACK_TYPE ),\r
+       /* Priority for every task pool thread. */\r
+       tskIDLE_PRIORITY,\r
+};\r
+\r
+/***************** Structures that define the connection. *********************/\r
+\r
+\r
+static const struct IotNetworkServerInfo xMQTTBrokerInfo =\r
+{\r
+       .pHostName = awsiotdemoprofileAWS_ENDPOINT,\r
+       .port = awsiotdemoprofileAWS_MQTT_PORT\r
+};\r
+\r
+static struct IotNetworkCredentials xNetworkSecurityCredentials =\r
+{\r
+       /* Optional TLS extensions. For this demo, they are disabled. */\r
+       .pAlpnProtos = NULL,\r
+       .maxFragmentLength = 0,\r
+\r
+       /* SNI is enabled by default. */\r
+       .disableSni = false,\r
+\r
+       /* Provide the certificate for validating the server. Only required for\r
+       demos using TLS. */\r
+       .pRootCa = awsiotdemoprofileAWS_CERTIFICATE_PEM,\r
+       .rootCaSize = sizeof( awsiotdemoprofileAWS_CERTIFICATE_PEM ),\r
+\r
+       /* Strong mutual authentication to authenticate both the broker and\r
+        * the client. */\r
+       .pClientCert = awsiotdemoprofileCLIENT_CERTIFICATE_PEM,\r
+       .clientCertSize = sizeof( awsiotdemoprofileCLIENT_CERTIFICATE_PEM ),\r
+       .pPrivateKey = awsiotdemoprofileCLIENT_PRIVATE_KEY_PEM,\r
+       .privateKeySize = sizeof( awsiotdemoprofileCLIENT_PRIVATE_KEY_PEM )\r
+};\r
+\r
+static IotMqttNetworkInfo_t xNetworkInfo =\r
+{\r
+       /* No connection to the MQTT broker has been established yet and we want to\r
+        * establish a new connection. */\r
+       .createNetworkConnection = true,\r
+       .u.setup.pNetworkServerInfo = &( xMQTTBrokerInfo ),\r
+\r
+       /* Set the TLS credentials for the new MQTT connection. */\r
+       .u.setup.pNetworkCredentialInfo = &xNetworkSecurityCredentials,\r
+\r
+       /* Use FreeRTOS+TCP network interface. */\r
+       .pNetworkInterface = IOT_NETWORK_INTERFACE_FREERTOS,\r
+\r
+       /* Setup the callback which is called when the MQTT connection is\r
+        * disconnected. The task handle is passed as the callback context which\r
+        * is used by the callback to send a task notification to this task.*/\r
+       .disconnectCallback.function = prvExample_OnDisconnect\r
+};\r
+\r
+static const IotMqttConnectInfo_t xConnectInfo =\r
+{\r
+       /* Set this flag to true if connecting to the AWS IoT MQTT broker. */\r
+       .awsIotMqttMode = false,\r
+\r
+       /* Start with a clean session i.e. direct the MQTT broker to discard any\r
+        * previous session data. Also, establishing a connection with clean session\r
+        * will ensure that the broker does not store any data when this client\r
+        * gets disconnected. */\r
+       .cleanSession = true,\r
+\r
+       /* Since we are starting with a clean session, there are no previous\r
+        * subscriptions to be restored. */\r
+       .pPreviousSubscriptions = NULL,\r
+       .previousSubscriptionCount = 0,\r
+\r
+       /* We do not want to publish Last Will and Testament (LWT) message if the\r
+        * client gets disconnected. */\r
+       .pWillInfo = NULL,\r
+\r
+       /* Send an MQTT PING request every minute to keep the connection open if\r
+        * there is no other MQTT traffic. */\r
+       .keepAliveSeconds = jobsexampleKEEP_ALIVE_SECONDS,\r
+\r
+       /* The client identifier is used to uniquely identify this MQTT client to\r
+        * the MQTT broker.  In a production device the identifier can be something\r
+        * unique, such as a device serial number. */\r
+       .pClientIdentifier = awsiotdemoprofileCLIENT_IDENTIFIER,\r
+       .clientIdentifierLength = ( uint16_t ) sizeof( awsiotdemoprofileCLIENT_IDENTIFIER ) - 1,\r
+\r
+       /* This example does not authenticate the client and therefore username and\r
+        * password fields are not used. */\r
+       .pUserName = NULL,\r
+       .userNameLength = 0,\r
+       .pPassword = NULL,\r
+       .passwordLength = 0\r
+};\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvExample_OnDisconnect( void * pvCallbackContext,\r
+                                                                        IotMqttCallbackParam_t * pxCallbackParams )\r
+{\r
+TaskHandle_t xDemoTaskHandle = ( TaskHandle_t ) pvCallbackContext;\r
+\r
+       /* Ensure that we initiated the disconnect. */\r
+       configASSERT( pxCallbackParams->u.disconnectReason == IOT_MQTT_DISCONNECT_CALLED );\r
+\r
+       /* Inform the demo task about the disconnect. */\r
+       xTaskNotify( xDemoTaskHandle,\r
+                                jobsexampleDISCONNECTED_BIT,\r
+                                eSetBits /* Set the jobsexampleDISCONNECTED_BIT in the demo task's notification value. */\r
+                                );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vStartJobsDemo( void )\r
+{\r
+TickType_t xShortDelay = ( TickType_t ) pdMS_TO_TICKS( ( TickType_t ) 500 );\r
+\r
+       /* Wait a short time to allow receipt of the ARP replies. */\r
+       vTaskDelay( xShortDelay );\r
+\r
+       /* This example uses a single application task, which in turn is used to\r
+        * connect, subscribe, publish, unsubscribe and disconnect from the MQTT\r
+        * broker. */\r
+       xTaskCreate( prvJobsDemoTask,             /* Function that implements the task. */\r
+                                "JobsDemo",                       /* Text name for the task - only used for debugging. */\r
+                                democonfigDEMO_STACKSIZE, /* Size of stack (in words, not bytes) to allocate for the task. */\r
+                                NULL,                                   /* Task parameter - not used in this case. */\r
+                                tskIDLE_PRIORITY,               /* Task priority, must be between 0 and configMAX_PRIORITIES - 1. */\r
+                                NULL );                                   /* Used to pass out a handle to the created task - not used in this case. */\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvJobsDemoTask( void * pvParameters )\r
+{\r
+IotMqttError_t xResult;\r
+IotNetworkError_t xNetworkInit;\r
+uint32_t ulNotificationValue = 0;\r
+const TickType_t xNoDelay = ( TickType_t ) 0;\r
+AwsIotJobsError_t xStatus = AWS_IOT_JOBS_SUCCESS;\r
+AwsIotJobsCallbackInfo_t xCallbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;\r
+AwsIotJobsRequestInfo_t xRequestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;\r
+\r
+       /* Remove compiler warnings about unused parameters. */\r
+       ( void ) pvParameters;\r
+\r
+       xMainTaskHandle = xTaskGetCurrentTaskHandle();\r
+\r
+       /* The MQTT library needs a task pool, so create the system task pool. */\r
+       xResult = IotTaskPool_CreateSystemTaskPool( &( xTaskPoolParameters ) );\r
+       configASSERT( xResult == IOT_TASKPOOL_SUCCESS );\r
+\r
+       /* Initialize the network stack abstraction for FreeRTOS. */\r
+       xNetworkInit = IotNetworkFreeRTOS_Init();\r
+       configASSERT( xNetworkInit == IOT_NETWORK_SUCCESS );\r
+\r
+       /* MQTT library must be initialized before it can be used. This is just one\r
+        * time initialization. */\r
+       xResult = IotMqtt_Init();\r
+       configASSERT( xResult == IOT_MQTT_SUCCESS );\r
+\r
+       /* Initialize Jobs library. */\r
+       xResult = AwsIotJobs_Init( jobsexampleUSE_DEFAULT_MQTT_TIMEOUT );\r
+       configASSERT( xResult == AWS_IOT_JOBS_SUCCESS );\r
+\r
+       /****************************** Connect. ******************************/\r
+\r
+       /* Establish a connection to the AWS IoT MQTT broker. This example connects to\r
+        * the MQTT broker as specified in awsiotdemoprofileAWS_ENDPOINT and\r
+        * awsiotdemoprofileAWS_MQTT_PORT at the top of this file.\r
+        */\r
+       configPRINTF( ( "Attempt to connect to %s\r\n", awsiotdemoprofileAWS_ENDPOINT ) );\r
+       prvMQTTConnect();\r
+       configPRINTF( ( "Connected to %s\r\n", awsiotdemoprofileAWS_ENDPOINT ) );\r
+\r
+       /* Don't expect any notifications to be pending yet. */\r
+       configASSERT( ulTaskNotifyTake( pdTRUE, xNoDelay ) == 0 );\r
+\r
+       configPRINTF( ( "Setting callback for jobs/notify-next\r\n" ) );\r
+       prvSetNotifyNextCallback();\r
+\r
+       /* Call DescribeAsync to see if there are any pending jobs. */\r
+       xRequestInfo.mqttConnection = xMQTTConnection;\r
+       xRequestInfo.pThingName = awsiotdemoprofileCLIENT_IDENTIFIER;\r
+       xRequestInfo.thingNameLength = jobsexampleCLIENT_IDENTIFIER_LENGTH;\r
+       xRequestInfo.pJobId = AWS_IOT_JOBS_NEXT_JOB;\r
+       xRequestInfo.jobIdLength = AWS_IOT_JOBS_NEXT_JOB_LENGTH;\r
+\r
+       /* Use the same callback as notify-next so any pending jobs will be\r
+        * executed the same way. */\r
+       xCallbackInfo.function = prvJobsCallback;\r
+\r
+       xStatus = AwsIotJobs_DescribeAsync( &xRequestInfo, AWS_IOT_JOBS_NO_EXECUTION_NUMBER, true, 0, &xCallbackInfo, NULL );\r
+       configPRINTF( ( "Describe queued with result %s.\r\n", AwsIotJobs_strerror( xStatus ) ) );\r
+\r
+       /* Print out a short user guide to the console. The default logging\r
+        * limit of 255 characters can be changed in demo_logging.c, but breaking\r
+        * up the only instance of a 1000+ character string is more practical. */\r
+       configPRINTF( (\r
+                                         "\r\n"\r
+                                         "/*-----------------------------------------------------------*/\r\n"\r
+                                         "\r\n"\r
+                                         "The Jobs demo is now ready to accept Jobs.\r\n"\r
+                                         "Jobs may be created using the AWS IoT console or AWS CLI.\r\n"\r
+                                         "See the following link for more information.\r\n"\r
+                                         "\r\n" ) );\r
+       configPRINTF( (\r
+                                         "\r"\r
+                                         "https://docs.aws.amazon.com/cli/latest/reference/iot/create-job.html\r\n"\r
+                                         "\r\n"\r
+                                         "This demo expects Job documents to have an \"action\" JSON key.\r\n"\r
+                                         "The following actions are currently supported:\r\n" ) );\r
+       configPRINTF( (\r
+                                         "\r"\r
+                                         " - print          \r\n"\r
+                                         "   Logs a message to the local console. The Job document must also contain a \"message\".\r\n"\r
+                                         "   For example: { \"action\": \"print\", \"message\": \"Hello world!\"} will cause\r\n"\r
+                                         "   \"Hello world!\" to be printed on the console.\r\n" ) );\r
+       configPRINTF( (\r
+                                         "\r"\r
+                                         " - publish        \r\n"\r
+                                         "   Publishes a message to an MQTT topic. The Job document must also contain a \"message\" and \"topic\".\r\n" ) );\r
+       configPRINTF( (\r
+                                         "\r"\r
+                                         "   For example: { \"action\": \"publish\", \"topic\": \"demo/jobs\", \"message\": \"Hello world!\"} will cause\r\n"\r
+                                         "   \"Hello world!\" to be published to the topic \"demo/jobs\".\r\n" ) );\r
+       configPRINTF( (\r
+                                         "\r"\r
+                                         " - exit           \r\n"\r
+                                         "   Exits the demo program. This program will run until { \"action\": \"exit\" } is received.\r\n"\r
+                                         "\r\n"\r
+                                         "/*-----------------------------------------------------------*/\r\n" ) );\r
+\r
+       /* Wait for an exit job to be received. If an exit job is not received within\r
+        * jobsexampleMS_BEFORE_EXIT, exit anyway. This is because we have disabled\r
+        * keep-alive, and the server will disconnect as after some time. */\r
+       xTaskNotifyWait( 0UL,                                      /* Don't clear any bits on entry. */\r
+                                        jobsexampleEXIT_BIT,      /* Clear bit on exit. */\r
+                                        &( ulNotificationValue ), /* Obtain the notification value. */\r
+                                        pdMS_TO_TICKS( jobsexampleMS_BEFORE_EXIT) );\r
+       /* Check was due to receiving an exit job. */\r
+       if( ( ulNotificationValue & jobsexampleEXIT_BIT ) != jobsexampleEXIT_BIT )\r
+       {\r
+               configPRINTF( ( "Disconnecting as %u milliseconds have elapsed.\r\n", jobsexampleMS_BEFORE_EXIT ) );\r
+       }\r
+\r
+       /* Disconnect MQTT gracefully. */\r
+       prvMQTTDisconnect();\r
+       configPRINTF( ( "Disconnected from %s\r\n\r\n", awsiotdemoprofileAWS_ENDPOINT ) );\r
+\r
+       /* Wait for the disconnect operation to complete which is informed to us\r
+        * by the disconnect callback (prvExample_OnDisconnect)by setting\r
+        * the jobsexampleDISCONNECTED_BIT in this task's notification value. */\r
+       xTaskNotifyWait( 0UL,                                             /* Don't clear any bits on entry. */\r
+                                        jobsexampleDISCONNECTED_BIT, /* Clear bit on exit. */\r
+                                        &( ulNotificationValue ),        /* Obtain the notification value. */\r
+                                        pdMS_TO_TICKS( jobsexampleMQTT_TIMEOUT_MS ) );\r
+       configASSERT( ( ulNotificationValue & jobsexampleDISCONNECTED_BIT ) == jobsexampleDISCONNECTED_BIT );\r
+\r
+       configPRINTF( ( "prvJobsDemoTask() completed successfully. Total free heap is %u\r\n", xPortGetFreeHeapSize() ) );\r
+       configPRINTF( ( "Demo completed successfully.\r\n" ) );\r
+\r
+       /* Clean up initialized libraries. */\r
+       AwsIotJobs_Cleanup();\r
+       IotMqtt_Cleanup();\r
+       IotNetworkFreeRTOS_Cleanup();\r
+\r
+       /* FreeRTOS Tasks must _vTaskDelete( NULL )_ before exiting the function. */\r
+       vTaskDelete( NULL );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvMQTTConnect( void )\r
+{\r
+IotMqttError_t xResult;\r
+\r
+       /* Set the context to pass into the disconnect callback function. */\r
+       xNetworkInfo.disconnectCallback.pCallbackContext = ( void * ) xTaskGetCurrentTaskHandle();\r
+\r
+       /* Establish the connection to the MQTT broker - It is a blocking call and\r
+        * will return only when connection is complete or a timeout occurs. */\r
+       xResult = IotMqtt_Connect( &( xNetworkInfo ),\r
+                                                          &( xConnectInfo ),\r
+                                                          jobsexampleMQTT_TIMEOUT_MS,\r
+                                                          &( xMQTTConnection ) );\r
+       configASSERT( xResult == IOT_MQTT_SUCCESS );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvMQTTDisconnect( void )\r
+{\r
+       /* Send a MQTT DISCONNECT packet to the MQTT broker to do a graceful\r
+        * disconnect. */\r
+       IotMqtt_Disconnect( xMQTTConnection,\r
+                                               0 /* flags - 0 means a graceful disconnect by sending MQTT DISCONNECT. */\r
+                                               );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvSetNotifyNextCallback( void )\r
+{\r
+AwsIotJobsError_t xCallbackStatus = AWS_IOT_JOBS_SUCCESS;\r
+AwsIotJobsCallbackInfo_t xCallbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;\r
+\r
+       /* Set the jobs callback function. */\r
+       xCallbackInfo.function = prvJobsCallback;\r
+\r
+       /************************ Set notify-next callbacks **********************/\r
+\r
+       xCallbackStatus = AwsIotJobs_SetNotifyNextCallback( xMQTTConnection,\r
+                                                                                                               awsiotdemoprofileCLIENT_IDENTIFIER,\r
+                                                                                                               jobsexampleCLIENT_IDENTIFIER_LENGTH,\r
+                                                                                                               0,\r
+                                                                                                               &xCallbackInfo );\r
+\r
+       configASSERT( xCallbackStatus == AWS_IOT_JOBS_SUCCESS );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static _jobAction_t prvGetAction( const char * pcAction,\r
+                                                                 size_t xActionLength )\r
+{\r
+_jobAction_t xAction = JOB_ACTION_UNKNOWN;\r
+\r
+       configASSERT( pcAction != NULL );\r
+\r
+       if( strncmp( pcAction, "print", xActionLength ) == 0 )\r
+       {\r
+               xAction = JOB_ACTION_PRINT;\r
+       }\r
+       else if( strncmp( pcAction, "publish", xActionLength ) == 0 )\r
+       {\r
+               xAction = JOB_ACTION_PUBLISH;\r
+       }\r
+       else if( strncmp( pcAction, "exit", xActionLength ) == 0 )\r
+       {\r
+               xAction = JOB_ACTION_EXIT;\r
+       }\r
+\r
+       return xAction;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static BaseType_t prvGetJsonString( const char * pcJsonDoc,\r
+                                                                       size_t xJsonDocLength,\r
+                                                                       const char * pcKey,\r
+                                                                       size_t xKeyLength,\r
+                                                                       const char ** ppcValue,\r
+                                                                       size_t * pxValueLength )\r
+{\r
+BaseType_t xKeyFound = pdFALSE;\r
+\r
+       configASSERT( pcJsonDoc != NULL );\r
+       configASSERT( pcKey != NULL );\r
+\r
+       /*\r
+        * Note: This parser used is specific for parsing AWS IoT document received\r
+        * through a mutually authenticated connection. This parser will not check\r
+        * for the correctness of the document as it is designed for low memory\r
+        * footprint rather than checking for correctness of the document. This\r
+        * parser is not meant to be used as a general purpose JSON parser.\r
+        */\r
+       xKeyFound = ( BaseType_t ) AwsIotDocParser_FindValue(\r
+               pcJsonDoc,\r
+               xJsonDocLength,\r
+               pcKey,\r
+               xKeyLength,\r
+               ppcValue,\r
+               pxValueLength );\r
+\r
+       if( xKeyFound == pdTRUE )\r
+       {\r
+               /* Exclude empty strings. */\r
+               if( *pxValueLength < jobsexampleJSON_STRING_MIN_LENGTH )\r
+               {\r
+                       xKeyFound = pdFALSE;\r
+               }\r
+               else\r
+               {\r
+                       /* Adjust the value to remove the quotes. */\r
+                       ( *ppcValue )++;\r
+                       ( *pxValueLength ) -= 2;\r
+               }\r
+       }\r
+\r
+       return xKeyFound;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvOperationCompleteCallback( void * pvCallbackContext,\r
+                                                                                 AwsIotJobsCallbackParam_t * pxCallbackParam )\r
+{\r
+       configASSERT( pxCallbackParam != NULL );\r
+\r
+       /* This function is invoked when either a StartNext or Update completes. */\r
+       if( pxCallbackParam->callbackType == AWS_IOT_JOBS_START_NEXT_COMPLETE )\r
+       {\r
+               configPRINTF( ( "Job StartNext complete with result %s.\r\n",\r
+                                               AwsIotJobs_strerror( pxCallbackParam->u.operation.result ) ) );\r
+       }\r
+       else\r
+       {\r
+               configPRINTF( ( "Job Update complete with result %s.\r\n",\r
+                                               AwsIotJobs_strerror( pxCallbackParam->u.operation.result ) ) );\r
+       }\r
+\r
+       /* If a non-NULL context is given, set the flag to exit the demo. */\r
+       if( pvCallbackContext != NULL )\r
+       {\r
+               xTaskNotify( xMainTaskHandle,\r
+                                        jobsexampleEXIT_BIT,\r
+                                        eSetBits /* Set the jobsexampleEXIT_BIT in the demo task's notification value. */\r
+                                        );\r
+       }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static AwsIotJobState_t prvProcessMessage( IotMqttConnection_t xMqttConnection,\r
+                                                                                  _jobAction_t xAction,\r
+                                                                                  const char * pcJobDoc,\r
+                                                                                  size_t xJobDocLength )\r
+{\r
+AwsIotJobState_t xStatus = AWS_IOT_JOB_STATE_SUCCEEDED;\r
+IotMqttError_t xMqttStatus = IOT_MQTT_STATUS_PENDING;\r
+IotMqttPublishInfo_t xPublishInfo = IOT_MQTT_PUBLISH_INFO_INITIALIZER;\r
+const char * pcMessage = NULL, * pcTopic = NULL;\r
+size_t xMessageLength = 0, xTopicLength = 0;\r
+\r
+       configASSERT( pcJobDoc != NULL );\r
+\r
+       /* Both "print" and "publish" require a "message" key. Search the Job\r
+        * document for this key. */\r
+       if( prvGetJsonString( pcJobDoc,\r
+                                                 xJobDocLength,\r
+                                                 jobsexampleMESSAGE_KEY,\r
+                                                 jobsexampleMESSAGE_KEY_LENGTH,\r
+                                                 &pcMessage,\r
+                                                 &xMessageLength ) == pdFALSE )\r
+       {\r
+               configPRINTF( ( "Job document for \"print\" or \"publish\" does not contain a %s key.\r\n",\r
+                                               jobsexampleMESSAGE_KEY ) );\r
+\r
+               xStatus = AWS_IOT_JOB_STATE_FAILED;\r
+       }\r
+\r
+       if( xStatus == AWS_IOT_JOB_STATE_SUCCEEDED )\r
+       {\r
+               if( xAction == JOB_ACTION_PRINT )\r
+               {\r
+                       /* Print the given message if the action is "print". */\r
+                       configPRINTF( (\r
+                                                         "\r\n"\r
+                                                         "/*-----------------------------------------------------------*/\r\n"\r
+                                                         "\r\n"\r
+                                                         "%.*s\r\n"\r
+                                                         "\r\n"\r
+                                                         "/*-----------------------------------------------------------*/\r\n"\r
+                                                         "\r\n", xMessageLength, pcMessage ) );\r
+               }\r
+               else\r
+               {\r
+                       /* Extract the topic if the action is "publish". */\r
+                       if( prvGetJsonString( pcJobDoc,\r
+                                                                 xJobDocLength,\r
+                                                                 jobsexampleTOPIC_KEY,\r
+                                                                 jobsexampleTOPIC_KEY_LENGTH,\r
+                                                                 &pcTopic,\r
+                                                                 &xTopicLength ) == pdFALSE )\r
+                       {\r
+                               configPRINTF( ( "Job document for action \"publish\" does not contain a %s key.\r\n",\r
+                                                               jobsexampleTOPIC_KEY ) );\r
+\r
+                               xStatus = AWS_IOT_JOB_STATE_FAILED;\r
+                       }\r
+\r
+                       if( xStatus == AWS_IOT_JOB_STATE_SUCCEEDED )\r
+                       {\r
+                               xPublishInfo.qos = IOT_MQTT_QOS_0;\r
+                               xPublishInfo.pTopicName = pcTopic;\r
+                               xPublishInfo.topicNameLength = ( uint16_t ) xTopicLength;\r
+                               xPublishInfo.pPayload = pcMessage;\r
+                               xPublishInfo.payloadLength = xMessageLength;\r
+\r
+                               xMqttStatus = IotMqtt_PublishAsync( xMqttConnection, &xPublishInfo, 0, NULL, NULL );\r
+\r
+                               if( xMqttStatus != IOT_MQTT_SUCCESS )\r
+                               {\r
+                                       xStatus = AWS_IOT_JOB_STATE_FAILED;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       return xStatus;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvProcessJob( const AwsIotJobsCallbackParam_t * pxJobInfo,\r
+                                                  const char * pcJobId,\r
+                                                  size_t xJobIdLength,\r
+                                                  const char * pcJobDoc,\r
+                                                  size_t xJobDocLength )\r
+{\r
+AwsIotJobsError_t xStatus = AWS_IOT_JOBS_SUCCESS;\r
+AwsIotJobsUpdateInfo_t xUpdateInfo = AWS_IOT_JOBS_UPDATE_INFO_INITIALIZER;\r
+AwsIotJobsCallbackInfo_t xCallbackInfo = AWS_IOT_JOBS_CALLBACK_INFO_INITIALIZER;\r
+const char * pcAction = NULL;\r
+size_t xActionLength = 0;\r
+_jobAction_t xAction = JOB_ACTION_UNKNOWN;\r
+AwsIotJobsRequestInfo_t xRequestInfo = AWS_IOT_JOBS_REQUEST_INFO_INITIALIZER;\r
+\r
+       configASSERT( pxJobInfo != NULL );\r
+       configASSERT( pcJobId != NULL );\r
+       configASSERT( pcJobDoc != NULL );\r
+\r
+       configPRINTF( ( "Job document received: %.*s\r\n", xJobDocLength, pcJobDoc ) );\r
+\r
+       xRequestInfo.mqttConnection = pxJobInfo->mqttConnection;\r
+       xRequestInfo.pThingName = pxJobInfo->pThingName;\r
+       xRequestInfo.thingNameLength = pxJobInfo->thingNameLength;\r
+       xRequestInfo.pJobId = pcJobId;\r
+       xRequestInfo.jobIdLength = xJobIdLength;\r
+\r
+       /* Tell the Jobs service that the device has started working on the Job.\r
+        * Use the StartNext API to set the Job's status to IN_PROGRESS. */\r
+       xCallbackInfo.function = prvOperationCompleteCallback;\r
+\r
+       xStatus = AwsIotJobs_StartNextAsync( &xRequestInfo, &xUpdateInfo, 0, &xCallbackInfo, NULL );\r
+\r
+       configPRINTF( ( "Jobs StartNext queued with result %s.\r\n", AwsIotJobs_strerror( xStatus ) ) );\r
+\r
+       /* Get the action for this device. */\r
+       if( prvGetJsonString( pcJobDoc,\r
+                                                 xJobDocLength,\r
+                                                 jobsexampleACTION_KEY,\r
+                                                 jobsexampleACTION_KEY_LENGTH,\r
+                                                 &pcAction,\r
+                                                 &xActionLength ) == pdTRUE )\r
+       {\r
+               xAction = prvGetAction( pcAction, xActionLength );\r
+\r
+               switch( xAction )\r
+               {\r
+                       case JOB_ACTION_EXIT:\r
+                               xCallbackInfo.pCallbackContext = jobsexampleSHOULD_EXIT;\r
+                               xUpdateInfo.newStatus = AWS_IOT_JOB_STATE_SUCCEEDED;\r
+                               break;\r
+\r
+                       case JOB_ACTION_PRINT:\r
+                       case JOB_ACTION_PUBLISH:\r
+                               xUpdateInfo.newStatus = prvProcessMessage( pxJobInfo->mqttConnection,\r
+                                                                                                                  xAction,\r
+                                                                                                                  pcJobDoc,\r
+                                                                                                                  xJobDocLength );\r
+                               break;\r
+\r
+                       default:\r
+                               configPRINTF( ( "Received Job document with unknown action %.*s.\r\n",\r
+                                                               xActionLength,\r
+                                                               pcAction ) );\r
+\r
+                               xUpdateInfo.newStatus = AWS_IOT_JOB_STATE_FAILED;\r
+                               break;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               configPRINTF( ( "Received Job document does not contain an %s key.\r\n",\r
+                                               jobsexampleACTION_KEY ) );\r
+\r
+               /* The given Job document is not valid for this demo. */\r
+               xUpdateInfo.newStatus = AWS_IOT_JOB_STATE_FAILED;\r
+       }\r
+\r
+       configPRINTF( ( "Setting state of %.*s to %s.\r\n",\r
+                                       xJobIdLength,\r
+                                       pcJobId,\r
+                                       AwsIotJobs_StateName( xUpdateInfo.newStatus ) ) );\r
+\r
+       /* Tell the Jobs service that the device has finished the Job. */\r
+       xStatus = AwsIotJobs_UpdateAsync( &xRequestInfo, &xUpdateInfo, 0, &xCallbackInfo, NULL );\r
+\r
+       configPRINTF( ( "Jobs Update queued with result %s.\r\n", AwsIotJobs_strerror( xStatus ) ) );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvJobsCallback( void * pCallbackContext,\r
+                                                        AwsIotJobsCallbackParam_t * pxCallbackInfo )\r
+{\r
+BaseType_t xIdKeyFound = pdFALSE, xDocKeyFound = pdFALSE;\r
+const char * pcJobId = NULL;\r
+size_t xJobIdLength = 0;\r
+const char * pcJobDoc = NULL;\r
+size_t xJobDocLength = 0;\r
+const char * pcRawDocument = NULL;\r
+size_t xRawDocumentLength = 0;\r
+\r
+       /* Silence warnings about unused parameters. */\r
+       ( void ) pCallbackContext;\r
+\r
+       configASSERT( pxCallbackInfo != NULL );\r
+\r
+       /* Check if this callback was called from a describe operation or\r
+        * due to notify-next. */\r
+       if( pxCallbackInfo->callbackType == AWS_IOT_JOBS_DESCRIBE_COMPLETE )\r
+       {\r
+               pcRawDocument = pxCallbackInfo->u.operation.pResponse;\r
+               xRawDocumentLength = pxCallbackInfo->u.operation.responseLength;\r
+       }\r
+       else\r
+       {\r
+               pcRawDocument = pxCallbackInfo->u.callback.pDocument;\r
+               xRawDocumentLength = pxCallbackInfo->u.callback.documentLength;\r
+       }\r
+\r
+       /* Get the Job ID. */\r
+       xIdKeyFound = prvGetJsonString( pcRawDocument,\r
+                                                                       xRawDocumentLength,\r
+                                                                       jobsexampleID_KEY,\r
+                                                                       jobsexampleID_KEY_LENGTH,\r
+                                                                       &pcJobId,\r
+                                                                       &xJobIdLength );\r
+\r
+       if( xIdKeyFound == pdTRUE )\r
+       {\r
+               if( xJobIdLength > jobsexampleID_MAX_LENGTH )\r
+               {\r
+                       configPRINTF( ( "Received Job ID %.*s longer than %lu, which is the "\r
+                                                       "maximum allowed by AWS IoT. Ignoring Job.\r\n",\r
+                                                       xJobIdLength,\r
+                                                       pcJobId,\r
+                                                       ( unsigned long ) jobsexampleID_MAX_LENGTH ) );\r
+\r
+                       xIdKeyFound = pdFALSE;\r
+               }\r
+               else\r
+               {\r
+                       configPRINTF( ( "Job %.*s received.\r\n", xJobIdLength, pcJobId ) );\r
+               }\r
+       }\r
+\r
+       /* Get the Job document.\r
+        *\r
+        * Note: This parser used is specific for parsing AWS IoT document received\r
+        * through a mutually authenticated connection. This parser will not check\r
+        * for the correctness of the document as it is designed for low memory\r
+        * footprint rather than checking for correctness of the document. This\r
+        * parser is not meant to be used as a general purpose JSON parser.\r
+        */\r
+       xDocKeyFound = ( BaseType_t ) AwsIotDocParser_FindValue(\r
+               pcRawDocument,\r
+               xRawDocumentLength,\r
+               jobsexampleDOC_KEY,\r
+               jobsexampleDOC_KEY_LENGTH,\r
+               &pcJobDoc,\r
+               &xJobDocLength );\r
+\r
+       /* When both the Job ID and Job document are available, process the Job. */\r
+       if( ( xIdKeyFound == pdTRUE ) && ( xDocKeyFound == pdTRUE ) )\r
+       {\r
+               /* Process the Job document. */\r
+               prvProcessJob( pxCallbackInfo,\r
+                                          pcJobId,\r
+                                          xJobIdLength,\r
+                                          pcJobDoc,\r
+                                          xJobDocLength );\r
+       }\r
+       else\r
+       {\r
+               /* The Jobs service sends an empty Job document when all Jobs are complete. */\r
+               if( ( xIdKeyFound == pdFALSE ) && ( xDocKeyFound == pdFALSE ) )\r
+               {\r
+                       configPRINTF( (\r
+                                                         "\r\n"\r
+                                                         "/*-----------------------------------------------------------*/\r\n"\r
+                                                         "\r\n"\r
+                                                         "All available Jobs complete.\r\n"\r
+                                                         "\r\n"\r
+                                                         "/*-----------------------------------------------------------*/\r\n"\r
+                                                         "\r\n" ) );\r
+               }\r
+               else\r
+               {\r
+                       configPRINTF( ( "Received an invalid Job document: %.*s\r\n",\r
+                                                       xRawDocumentLength,\r
+                                                       pcRawDocument ) );\r
+               }\r
+       }\r
+}\r
+/*-----------------------------------------------------------*/\r