Search code examples
arduinoesp8266arduino-c++

Constructs in C++/Arduino


I have a question about the C++ Constructs(It's not an issue :) ) I am building a project for ESP controllers that have to perform tasks. The tasks are received by the ESP from HTTP Request in JSON format and I decode the JSON using ArduinoJSON. In order to use the tasks list, and in order to be more clear I am defining Construct, containing the options. Before the request I do not know the number of tasks.

While testing I've allocated the size of the construct to: 1 tasks tasks[1]; But have passed 4 tasks in the Setup. The thing I do not understand is why after I serial print them I get: 1 2 3 4

I expected different behaviour, the drop of all tasks in the construct after the first one. Can you help me understand it better?

This is the sample code:

#include <ArduinoJson.h>

struct tasks
{
    uint32_t id,duration;
    uint8_t nextTask,pin,interrupt,status;
    byte state,callback;  
};

byte operation;
uint8_t number_of_tasks=0;

String JSON = "{\"tasks\":[{\"task_id\":\"1\",\"pin\":\"13\",\"state\":\"1\",\"duration\":\"5\",\"interrupt\":\"0\",\"callback\":\"1\",\"status\":\"0\"},{\"task_id\":\"2\",\"pin\":\"13\",\"state\":\"1\",\"duration\":\"5\",\"interrupt\":\"0\",\"callback\":\"1\",\"status\":\"0\"},{\"task_id\":\"3\",\"pin\":\"13\",\"state\":\"1\",\"duration\":\"5\",\"interrupt\":\"0\",\"callback\":\"1\",\"status\":\"0\"},{\"task_id\":\"4\",\"pin\":\"13\",\"state\":\"1\",\"duration\":\"5\",\"interrupt\":\"0\",\"callback\":\"1\",\"status\":\"0\"}]}";
tasks tasks[1];


void setup() 
{
    // put your setup code here, to run once:
    Serial.begin(115200);
    delay(500);
    Serial.println();

    StaticJsonDocument<1000> doc;

    DeserializationError error = deserializeJson(doc, JSON);

    if (error) 
    {
        Serial.print(F("deserializeJson() failed: "));
        Serial.println(error.f_str());
        
        return;
    }

    for (JsonObject task : doc["tasks"].as<JsonArray>()) 
    {
        tasks[number_of_tasks].id = task["task_id"];
        tasks[number_of_tasks].pin = task["pin"];  
        tasks[number_of_tasks].state = task["state"];    
        tasks[number_of_tasks].duration = task["duration"];  
        tasks[number_of_tasks].interrupt = task["interrupt"];  
        tasks[number_of_tasks].callback = task["callback"];  
        tasks[number_of_tasks].status = task["status"];        
        number_of_tasks++;
    }

    for(int i=0;i<number_of_tasks;i++)
    {
        Serial.println(tasks[i].id);
    }
}

Solution

  • As NathanOliver mentioned in his comment you are in the undefined behavior land. As a fun fact, your code might even be unpredictable at the moment. Now it may insert the elements but once you add more code, there's a chance of it crashing etc. Those are all undefined behaviors. Now to address your problem and how you can fix it:

    You can use the std::vector<tasks> as NathanOliver suggested. That comes with some small overhead that you might not want to have in your arduino project. But it is the safest thing you can do without having to check if you need to allocate more space.

    The other option I can think of (when you know exactly how many tasks you wish to process), is to just predefine (using constexpr) how many tasks you wish to have i.e.:

    constexpr int max_number_of_tasks = 2;
    tasks task[max_number_of_tasks]; 
    

    and in the loop you can do a simple bounds check and break:

    for(JsonObject task : doc["tasks"].as<JsonArray>()) {
      if (number_of_tasks > max_number_of_tasks) {
        break;
      }
    ...
    

    Hope it helps!

    EDIT: You can ofcourse go down the road of allocating more space for the next tasks. But if that's the case then STL containers are IMHO a better option.