vTaskDelay & aws_iot_shadow_yield - ESP32, FreeRTOS, IoT Device Shadow

IainWoolley wrote on October 17, 2019:

I’m setting up a thing (ESP32 running FreeRTOS) that reports its state in a device shadow every 60 seconds, but needs to trigger a delta callback as soon as the desired state in the device shadow changes. Do I need to use 2 tasks to achieve this? One task that updates the shadow and I can put to sleep with vTaskDelay(), and a second task that calls aws_iot_shadow_yield() to enable the mqtt client to check for shadow updates from IoT Core?

Right now, when I call vTaskDelay() the task goes to sleep and the delta callback is only triggered when the task wakes back up 60 seconds later. If I create 2 tasks, then I get a SSL error

E (18490) aws_iot: failed! mbedtls_ssl_handshake returned -0x10

If I create 1 task which connects to AWS then goes into a loop calling aws_iot_shadow_yield(), and use that task to create a 2nd task, which goes into a loop calling vTaskDelay() and aws_iot_shadow_update(), then I don’t get the SSL error, but I do get

MQTT_CLIENT_NOT_IDLE_ERROR

I have got ENABLE_THREAD_SUPPORT defined in my aws_iot_config.h file, so I’m at a loss as to how to progress.

Any ideas?

qiutongs wrote on October 17, 2019:

Which version of Amazon FreeRTOS or IoT C-SDK are you using? It seems you are using an old version our shadow library which has the aws_iot_shadow_yield function.

We recommend to you to try our latest shadow library in “v4_beta” branch of IoT C-SDK and “master” branch of Amazon FreeRTOS, listed below. With that, you don’t need two tasks. New Shadow library provides a function AwsIotShadow_SetDeltaCallback, which fits your need.

IainWoolley wrote on October 18, 2019:

I’m using v3.0.1 of the IoT SDK and v8.2.0 of FreeRTOS. This is because I’m using Espressif’s ESP-IDF, and v8.2.0 is the base version of FreeRTOS they’ve used which supports SMP.

Below is some pseudocode to illustrate my initial approach, which only triggers the delta callback after the vTaskDelay() call:


ShadowInitParameters_t shadowInitParams = ShadowInitParametersDefault;
ShadowConnectParameters_t shadowConnectParams = ShadowConnectParametersDefault;
jsonStruct_t shadowParam2, shadowParam2, shadowParam3;

void shadowInit()
  {
  shadowInitParams = // host address, port, certs, etc.
  shadowConnectParams = // thing name, client id, etc. 

  // set up shadowParam, includng delta callback function
  shadowParam1.cb = cbShadowDelta;
  shadowParam1.pData = &parameter_1;
  shadowParam1.pKey = "parameter_1";
  shadowParam1.type = SHADOW_JSON_FLOAT;
  shadowParam1.dataLength = sizeof(float_t);
  // and the same for shadowParam2, shadowParam3

  // create a task to run the shadow
  xTaskCreatePinnedToCore(&shadowTask, "shadow_task", 5120, NULL, 5, NULL, 1);
  }

void shadowTask(void *param)
  {
  // initialise the shadow connection
  aws_iot_shadow_init(&mqttClient, shadowInitParams);

  // connect the shadow
  aws_iot_shadow_connect(&mqttClient, &shadowConnectParams);

  // register the delta callbacks
  aws_iot_shadow_register_delta(&mqttClient, &shadowParam1);
  aws_iot_shadow_register_delta(&mqttClient, &shadowParam2);
  aws_iot_shadow_register_delta(&mqttClient, &shadowParam3);

  while(networkConnected == true)
    {
    // wait for reporting period to elapse
    vTaskDelay(reportingPeriod);

    // yield to let MQTT receive message
    aws_iot_shadow_yield(&mqttClient, 250);

    // initialise the shadow document
    aws_iot_shadow_init_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer);

    // add the parameters to the shadow document
    aws_iot_shadow_add_reported(JsonDocumentBuffer, sizeOfJsonDocumentBuffer, 3, &shadowParam1, &shadowParam2, &shadowParam3);

    // finalise the shadow document
    aws_iot_finalize_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer);

    // update the shadow document
    aws_iot_shadow_update(&mqttClient, clientId, JsonDocumentBuffer, cbShadowUpdateStatus, NULL, 5, true);
    }
  }
 
void cbShadowDelta(const char *pJsonString, uint32_t valueLength, jsonStruct_t *pContext)
  {
  // react to the new desired state
  }

void cbShadowUpdateStatus(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status, const char *pReceivedJsonDocument, void *pContextData) 
  {
  if(SHADOW_ACK_ACCEPTED == status)
    {
    // it's all good
    }
  }  

IainWoolley wrote on October 18, 2019:

Ok, current solution is indeed 2 tasks. The existing loop in shadowTask() has the vTaskDelay() call removed, and just calls aws_iot_shadow_yield(). This means the task doesn’t go to sleep and the MQTT client can process incoming messages, and trigger the delta callbacks as required.
In a second task there’s a slower loop, using vTaskDelay(), which sets a bit in an event group to signal the first task that it is time to update the shadow. So the calls to prepare and send the shadow document are wrapped in a check for the bit in the event group, and then reset that bit.