Publishing JSON object through MQTT to AWS IoT from ESP32 using C and ESP-IDF framework

2.4k Views Asked by At

I have a structure IoT_Publish_Message_Params that needs to be prepared in order to prepare a publishing on AWS IoT. The below snippet works completely fine when I'm passing a string as the payload.

/**
 * @brief Publish Message Parameters Type
 *
 * Defines a type for MQTT Publish messages. Used for both incoming and out going messages
 *
 */
typedef struct {
    QoS qos;        ///< Message Quality of Service
    uint8_t isRetained; ///< Retained messages are \b NOT supported by the AWS IoT Service at the time of this SDK release.
    uint8_t isDup;      ///< Is this message a duplicate QoS > 0 message?  Handled automatically by the MQTT client.
    uint16_t id;        ///< Message sequence identifier.  Handled automatically by the MQTT client.
    void *payload;      ///< Pointer to MQTT message payload (bytes).
    size_t payloadLen;  ///< Length of MQTT payload.
} IoT_Publish_Message_Params;

IoT_Publish_Message_Params paramsQOS0;
sprintf(cPayload, "%s : %d ", "Hello from HOME!!", i);

paramsQOS0.qos = QOS0;
paramsQOS0.payload = (void *) cPayload;
paramsQOS0.isRetained = 0;
paramsQOS0.payloadLen = strlen(cPayload);
rc = aws_iot_mqtt_publish(&client, TOPIC, TOPIC_LEN, &paramsQOS0);

Now I want to send an actual JSON payload and I'm not sure how I can do that. I've tried using cJSON to create a JSON object:

cJSON *root,*fmt;
root=cJSON_CreateObject();
paramsQOS0.payload = (void *) root
cJSON_AddItemToObject(root, "response", fmt=cJSON_CreateObject());
cJSON_AddNumberToObject(fmt,"hello", 123);
cJSON_AddNumberToObject(fmt,"bye", 321);

However, my question is, what do I pass as IoT_Publish_Message_Params.payloadLen here? And how do I pass the json object to IoT_Publish_Message_Params.payload?

2

There are 2 best solutions below

0
On

As I see it you have two options. Send the JSON as a string, or as raw bytes.

If you want to send it as a string (e.g. {"CarType":"BMW","carID":"bmw123"}), then you want to convert it to a string. Found some code here.

char* str = cJSON_Print(root);  
paramsQOS0.payload = (void *) str;
paramsQOS0.payloadLen = strlen(str);

However, it would be much more efficient to send it as raw bytes. For this, you would need a pointer to the start of the object, and the size of the object in bytes. Quickly scanning the GitHub Page I found the cJSON_GetArraySize which apparently returns the object size.

Then it should look something like this:

paramsQOS0.payload = (void *) root;
paramsQOS0.payloadLen = cJSON_GetArraySize(root); // Not sure about this one

Disclaimer, I haven't used cJSON and haven't tested the code. I'm trying to show you which direction to take.

0
On

IoT_Publish_Message_Params has payload and payloadLen members, so you can provide any valid memory and length of bytes you want to send. In your first example you provided a statically allocated string and it's length.

JSON is a string based encoding protocol, so payload will be a char pointer and payloadLen can be calculated using strlen.

cJSON_Print returns the json string for an object, use the pointer returned by it and assign it to payload.

Please read cJSON API documentation how to use it. And below is how you can use modify your code to send the JSON string.

IoT_Publish_Message_Params paramsQOS0;
char *json_str;

/* create a json message */
cJSON *root, *response;
root = cJSON_CreateObject();
response = cJSON_CreateObject();
cJSON_AddItemToObject(root, "response", response);
cJSON_AddNumberToObject(response, "hello", 123);
cJSON_AddNumberToObject(response, "bye", 321);

json_str = cJSON_Print(root);
if (json_str != NULL)
{
    paramsQOS0.qos = QOS0;
    paramsQOS0.isRetained = 0;
    paramsQOS0.payload = json_str;
    paramsQOS0.payloadLen = strlen(json_str);
    rc = aws_iot_mqtt_publish(&client, TOPIC, TOPIC_LEN, &paramsQOS0);
    free(json_str);
}

cJSON_Delete(root);