Hi there, I'm writing code to control a fitness device. For this purpose I'm using an ESP32 which is connected a local MQTT broker from which it receives it's messages.
On the callback messageReceived I'm creating a new task.
void messageReceived(String &topic, String &payload) {
xTaskCreate(
vParseAndProcessJsonWorkout,
"Parse Workout",
20000,
(char*) payload.c_str(),
4,
NULL);
}
The task - pasted further below - receives the mqtt payload. See terminal output below. I can't seem to figure out why the json string is received in the vParseAndProcessJsonWorkout task but when calling size() and or accessing values all is returned as null?
[WIFI] Connecting
[WIFI] Connected: 10.1.20.21
[MQTT] Connecting...
[MQTT] Connected!
"{\"workout_id\":12123434,\"name\":\"Easy Starter\",\"segment\":[{\"repeat\":1,\"steps\":[{\"pace\":130,\"time\":10}]},{\"repeat\":2,\"steps\":[{\"pace\":130,\"time\":10},{\"pace\":130,\"time\":10},{\"pace\":130,\"time\":10}]}]}"
0
196
0
#ifndef TASK_WORKOUT_PROCESSOR
#define TASK_WORKOUT_PROCESSOR
#include <Arduino.h>
#include <ArduinoJson.h>
#include "include/config.h"
#include "include/helpers.h"
void vParseAndProcessJsonWorkout( void* pvParameters)
{
serial_println((char*) pvParameters);
DynamicJsonDocument doc(2048);
DeserializationError err = deserializeJson(doc, (char*) pvParameters);
if (err) {
serial_print(F("deserializeJson() failed with code "));
serial_println(err.c_str());
}
serial_println((bool) doc.isNull());
serial_println((size_t) doc.memoryUsage());
serial_println((size_t) doc["segment"].size());
JsonArray segments = doc["segment"].to<JsonArray>();
for (JsonObject segment : segments) {
int repeats = segment["repeat"]; // 1
serial_println(repeats);
// etc.
}
vTaskDelete(NULL);
}
#endif
Hi @nickels,
I'm not familiar with FreeRTOS, but I know there is something wrong here:
void messageReceived(String &topic, String &payload) {
xTaskCreate(
vParseAndProcessJsonWorkout,
"Parse Workout",
20000,
(char*) payload.c_str(), // <--- HERE
4,
NULL);
}
This function enqueues a task with a pointer to the internal buffer of a String.
This buffer is likely to be freed or reallocated before the task executes.
Sure, it's printed correctly, but that may be because the heap wasn't touched.
Then, when you create the DynamicJsonDocument, it starts to write to the heap, erasing the bytes of the original string.
You could fix this program with something like that:
void messageReceived(const char* topic, const char* payload) {
xTaskCreate(
vParseAndProcessJsonWorkout,
"Parse Workout",
20000,
strdup(payload), // call free(pvParameters) in vParseAndProcessJsonWorkout()
4,
NULL);
}
Best regards,
Benoit
HI Benoit,
First of all thank you for your help. It is very much apreciated, the world of C++ is still quite daunting.
I've made the following changes:
(switched to advanced callback for mqtt message)
void messageReceivedAdvanced(MQTTClient *client, char topic[], char payload[], int payload_length) {
xTaskCreate(
vParseAndProcessJsonWorkout,
"Parse Workout",
20000,
strdup(payload),
4,
NULL);
}
void vParseAndProcessJsonWorkout( void* pvParameters)
{
DynamicJsonDocument doc(2048);
DeserializationError err = deserializeJson(doc, (const char*)pvParameters);
if (err) {
serial_print(F("deserializeJson() failed with code "));
serial_println(err.c_str());
}
serial_println();
serializeJson(doc, serial_);
serial_println();
serial_println((bool) doc.isNull());
serial_println((size_t) doc.memoryUsage());
serial_println((size_t) doc.size());
free(pvParameters);
vTaskDelete(NULL);
}
Terminal output
[MQTT] Connected!
"{\"workout_id\":12123434,\"name\":\"Easy Starter\",\"segment\":[{\"repeat\":1,\"steps\":[{\"pace\":130,\"time\":10}]},{\"repeat\":2,\"steps\":[{\"pace\":130,\"time\":10},{\"pace\":130,\"time\":10},{\"pace\":130,\"time\":10}]}]}" (--- output from serializeJson(doc, serial_); ---)
0 (--- output from serial_println((bool) doc.isNull()); ---)
196 (--- output from serial_println((size_t) doc.memoryUsage()); ---)
0 (--- output from serial_println((size_t) doc.size()); ---)
So the size is not 0 anymore, that's good I suppose? Question remaining why the size is returning 0.
Thanks again for your help :-)
N
Hi N,
JsonDocument::size() returns the number of elements if the document contains an array or the number of members if it contains an object.
In your case, it's neither an array nor an object: it's a string.
If you cannot send the code on the sending side, you need to call deserializeJson(doc2, doc.as<char*>()) to parse the object in the string.
I'm sorry I didn't notice this issue in the first message but my attention got drawn by the dangling pointer.
Best regards,
Benoit
Wooohoooo!! Thank you for your help!! Now it's time to fully read the book!
Most helpful comment
Hi N,
JsonDocument::size()returns the number of elements if the document contains an array or the number of members if it contains an object.In your case, it's neither an array nor an object: it's a string.
If you cannot send the code on the sending side, you need to call
deserializeJson(doc2, doc.as<char*>())to parse the object in the string.I'm sorry I didn't notice this issue in the first message but my attention got drawn by the dangling pointer.
Best regards,
Benoit