Struct with an array returns random values

Hello

Recently I got my hands on NodeMCU esp8266. I am trying to build an alarm system. I have two structs Alarm and AlarmInfo. Here is the code:

struct Alarm {
  private:
    int alarmHour;// 00 - 23;
    int alarmMinute; // 00 - 59;
    int duration; //undefined
  public:
    Alarm(int newAlarmHour, int newAlarmMinute, int newDuration) {
      alarmHour = newAlarmHour;
      alarmMinute = newAlarmMinute;
      duration = newDuration;
    }
    Alarm() {}
    int getAlarmHour() {
      return alarmHour;
    }
    int getAlarmMinute() {
      return alarmMinute;
    }
    int getAlarmDuration() {
      return duration;
    }
    //    void setHour(int newHour); // might not use just in case;
    //    void setMinute(int newMinute);
    String toString() { // returns string representation only of the alarm (format: hh:mm)
      char buffer[10];
      sprintf(buffer, "%02u:%02u", alarmHour, alarmMinute);
      return String(buffer);
    }
    String toStringDuration() { // returns string representation with duration (format: hh:mm/dd)
      char buffer[20];
      sprintf(buffer, "%02u:%02u:%d", alarmHour, alarmMinute, duration);
      return String(buffer);
    }
};
struct AlarmInfo {
  private:
    String name;
    Alarm *alarms;
    int alarmSize;
    int *days;
    int daySize;
    boolean isActive;
  public:
    AlarmInfo() {}
    AlarmInfo(String newName, Alarm newAlarms[], int newAlarmSize, int newDays[], int newDaySize, boolean newIsActive) {
      name = newName;
      alarms = newAlarms;
      alarmSize = newAlarmSize;
      days = newDays;
      daySize = newDaySize;
      isActive = newIsActive;
    }
    Alarm* getAlarms() {
      return alarms;
    };
    void readVals() {
      Alarm *temp = alarms;
      Serial.print("Size: ");
      Serial.println(alarmSize);
      for (int i = 0; i < alarmSize; i++) {
        Serial.println(temp++->toStringDuration());
      }
    }
};

I call this function which parses the json correctly but when I call

for(int i = 0;i<jsonArray.size();i++){
    alarmInfos[i].readVals();
  }

I get random values.
Here's the function and output:

void loadAlarms(JsonArray jsonArray) {
  //Parsing the Json
  AlarmInfo alarmInfos[jsonArray.size()];
  for (int i = 0; i < jsonArray.size(); i++) {
    JsonObject object = jsonArray[i].as<JsonObject>();
    String alarmInfoName = object["Name"].as<String>();
    JsonArray alarmsJsonArray = object["Alarms"].as<JsonArray>();
    Alarm alarmArray[alarmsJsonArray.size()];
    for (int j = 0; j < alarmsJsonArray.size(); j++) {
      JsonObject alarmObject = alarmsJsonArray[j].as<JsonObject>();
      int alarmHour = alarmObject["hour"];
      int alarmMinute = alarmObject["minute"];
      int duration = alarmObject["duration"];

      alarmArray[j] = Alarm(alarmHour, alarmMinute, duration);
    }
    JsonArray daysJsonArray = object["Days"].as<JsonArray>();
    int daysArray[daysJsonArray.size()];
    for (int k = 0; k < daysJsonArray.size(); k++) {
      daysArray[k] = daysJsonArray[k].as<int>();
    }
    bool isActive = object["Active"].as<bool>();
    alarmInfos[i] = AlarmInfo(alarmInfoName, alarmArray, alarmsJsonArray.size(),
                              daysArray, daysJsonArray.size(), isActive);
    //This gives correct values
    alarmInfos[i].readVals();
  }

  for(int i = 0;i<jsonArray.size();i++){
    alarmInfos[i].readVals();
  }
}
Size: 6
12:30:5
13:30:5
14:30:5
15:30:5
16:30:5
17:30:5
Size: 6
17:30:5
17:30:5
18:30:5
19:30:5
20:30:5
21:30:5
Size: 6
17:30:5
17:30:5
18:30:5
19:30:5
20:30:5
21:30:5
Size: 6
17:30:5
17:30:5
18:30:5
19:30:5
20:30:5
21:30:5
Size: 6
17:1073672900:107373
1075862441:825898801
943337780:959984946:
00:1310751:0
20:30:5
1073736416:06:0
Size: 6
17:1073672900:107373
1075862441:825898801
943337780:959984946:
00:1310751:0
20:30:5
1073736416:06:0
Size: 6
17:1073672900:107373
1075862441:825898801
943337780:959984946:
00:1310751:0
20:30:5
1073736416:06:0
Size: 6
17:1073672900:107373
1075862441:825898801
943337780:959984946:
00:1310751:0
20:30:5
1073736416:06:0

I tried the same function in C++ and it worked fine.

EDIT
Here is the whole sketch:

#include <FS.h>
#include <LittleFS.h>


#include "AsyncJson.h"
#include "ArduinoJson.h"

//Alarm Struct
struct Alarm {
  private:
    int alarmHour;// 00 - 23;
    int alarmMinute; // 00 - 59;
    int duration; //undefined
  public:
    Alarm(int newAlarmHour, int newAlarmMinute, int newDuration) {
      alarmHour = newAlarmHour;
      alarmMinute = newAlarmMinute;
      duration = newDuration;
    }
    Alarm() {}
    int getAlarmHour() {
      return alarmHour;
    }
    int getAlarmMinute() {
      return alarmMinute;
    }
    int getAlarmDuration() {
      return duration;
    }
    String toString() { // returns string representation only of the alarm (format: hh:mm)
      char buffer[10];
      sprintf(buffer, "%02u:%02u", alarmHour, alarmMinute);
      return String(buffer);
    }
    String toStringDuration() { // returns string representation with duration (format: hh:mm/dd)
      char buffer[20];
      sprintf(buffer, "%02u:%02u:%d", alarmHour, alarmMinute, duration);
      return String(buffer);
    }
};

struct AlarmInfo {
  private:
    String name;
    Alarm *alarms;
    int alarmSize;
    int *days;
    int daySize;
    boolean isActive;
  public:
    AlarmInfo() {}
    AlarmInfo(String newName, Alarm newAlarms[], int newAlarmSize, int newDays[], int newDaySize, boolean newIsActive) {
      name = newName;
      alarms = newAlarms;
      alarmSize = newAlarmSize;
      days = newDays;
      daySize = newDaySize;
      isActive = newIsActive;
    }
    Alarm* getAlarms() {
      return alarms;
    };
    void readVals() {
      Alarm *temp = alarms;
      Serial.print("Size: ");
      Serial.println(alarmSize);
      for (int i = 0; i < alarmSize; i++) {
        Serial.println(temp++->toStringDuration());
      }
    }
};


void setup() {
  Serial.begin(115200);
  Serial.println("Hello There");
  
  LittleFS.begin();
  File file = LittleFS.open("/presetdata.json", "r");
  if(!file) {
    Serial.println("Cannot read the file");
  }
  DynamicJsonDocument data(8192);
  deserializeJson(data, file);
  loadAlarms(data.as<JsonArray>());

}

void loop() {
}


void loadAlarms(JsonArray jsonArray) {
  AlarmInfo alarmInfos[jsonArray.size()];
  ////////////////////////Parsing JSON////////////////////////////////
  for (int i = 0; i < jsonArray.size(); i++) {
    JsonObject object = jsonArray[i].as<JsonObject>();
    String alarmInfoName = object["Name"].as<String>();
    JsonArray alarmsJsonArray = object["Alarms"].as<JsonArray>();
    Alarm alarmArray[alarmsJsonArray.size()];
    for (int j = 0; j < alarmsJsonArray.size(); j++) {
      JsonObject alarmObject = alarmsJsonArray[j].as<JsonObject>();
      int alarmHour = alarmObject["hour"];
      int alarmMinute = alarmObject["minute"];
      int duration = alarmObject["duration"];

      alarmArray[j] = Alarm(alarmHour, alarmMinute, duration);
    }
    JsonArray daysJsonArray = object["Days"].as<JsonArray>();
    int daysArray[daysJsonArray.size()];
    for (int k = 0; k < daysJsonArray.size(); k++) {
      daysArray[k] = daysJsonArray[k].as<int>();
    }
    bool isActive = object["Active"].as<bool>();
//////////////////////////////////////////////////////////////////////
    alarmInfos[i] = AlarmInfo(alarmInfoName, alarmArray, alarmsJsonArray.size(),
                              daysArray, daysJsonArray.size(), isActive);
    alarmInfos[i].readVals();
  }
  for (int i = 0; i < jsonArray.size(); i++) {
    alarmInfos[i].readVals();
  }
}


1 Like

What does that mean? The code you posted is C++.

Rather than making us try to mentally piece your complete code together from disjoint snippets, post an MRE (Minimal Reproducible Example). This is the smallest complete code that compiles and demonstrates the problem at hand. Leave out all extraneous clutter.

I run the provided code inside dev-c++ and it worked fine

Your 'alarmInfos' array is local to the loadAlarms() function. Once you leave the function, the memory containing the struct is free to be re-allocated. To put data into memory that will still be usable after the function ends you have to allocate memory dynamically (with 'new') or make the variables global and make sure you allocate enough space for all possible numbers of alarms.

1 Like

And additionally (in a cross post):

https://forum.arduino.cc/t/struct-array-returns-random-values/1019239/5

Recently I got my hands on NodeMCU esp8266. I am trying to build an alarm system. I have two structs Alarm and AlarmInfo. What I'm trying to do is read the data from the File system, parse it and use it to determine the active preset (AlarmInfo). Each Preset can have multiple alarms and I use an array of alarms. I don't know what am I doing wrong. When I tried to run the same code in dev-c++ (with some changes to the output) it worked. Here is the code:

#include <FS.h>
#include <LittleFS.h>


#include "AsyncJson.h"
#include "ArduinoJson.h"

//Alarm Struct
struct Alarm {
  private:
    int alarmHour;// 00 - 23;
    int alarmMinute; // 00 - 59;
    int duration; //undefined
  public:
    Alarm(int newAlarmHour, int newAlarmMinute, int newDuration) {
      alarmHour = newAlarmHour;
      alarmMinute = newAlarmMinute;
      duration = newDuration;
    }
    Alarm() {}
    int getAlarmHour() {
      return alarmHour;
    }
    int getAlarmMinute() {
      return alarmMinute;
    }
    int getAlarmDuration() {
      return duration;
    }
    String toString() { // returns string representation only of the alarm (format: hh:mm)
      char buffer[10];
      sprintf(buffer, "%02u:%02u", alarmHour, alarmMinute);
      return String(buffer);
    }
    String toStringDuration() { // returns string representation with duration (format: hh:mm/dd)
      char buffer[20];
      sprintf(buffer, "%02u:%02u:%d", alarmHour, alarmMinute, duration);
      return String(buffer);
    }
};

struct AlarmInfo {
  private:
    String name;
    Alarm *alarms;
    int alarmSize;
    int *days;
    int daySize;
    boolean isActive;
  public:
    AlarmInfo() {}
    AlarmInfo(String newName, Alarm newAlarms[], int newAlarmSize, int newDays[], int newDaySize, boolean newIsActive) {
      name = newName;
      alarms = newAlarms;
      alarmSize = newAlarmSize;
      days = newDays;
      daySize = newDaySize;
      isActive = newIsActive;
    }
    Alarm* getAlarms() {
      return alarms;
    };
    void readVals() {
      Alarm *temp = alarms;
      Serial.print("Size: ");
      Serial.println(alarmSize);
      for (int i = 0; i < alarmSize; i++) {
        Serial.println(temp++->toStringDuration());
      }
    }
};


void setup() {
  Serial.begin(115200);
  Serial.println("Hello There");
  
  LittleFS.begin();
  File file = LittleFS.open("/presetdata.json", "r");
  if(!file) {
    Serial.println("Cannot read the file");
  }
  DynamicJsonDocument data(8192);
  deserializeJson(data, file);
  loadAlarms(data.as<JsonArray>());

}

void loop() {
}


void loadAlarms(JsonArray jsonArray) {
  AlarmInfo alarmInfos[jsonArray.size()];
  ////////////////////////Parsing JSON////////////////////////////////
  for (int i = 0; i < jsonArray.size(); i++) {
    JsonObject object = jsonArray[i].as<JsonObject>();
    String alarmInfoName = object["Name"].as<String>();
    JsonArray alarmsJsonArray = object["Alarms"].as<JsonArray>();
    Alarm alarmArray[alarmsJsonArray.size()];
    for (int j = 0; j < alarmsJsonArray.size(); j++) {
      JsonObject alarmObject = alarmsJsonArray[j].as<JsonObject>();
      int alarmHour = alarmObject["hour"];
      int alarmMinute = alarmObject["minute"];
      int duration = alarmObject["duration"];

      alarmArray[j] = Alarm(alarmHour, alarmMinute, duration);
    }
    JsonArray daysJsonArray = object["Days"].as<JsonArray>();
    int daysArray[daysJsonArray.size()];
    for (int k = 0; k < daysJsonArray.size(); k++) {
      daysArray[k] = daysJsonArray[k].as<int>();
    }
    bool isActive = object["Active"].as<bool>();
//////////////////////////////////////////////////////////////////////
    alarmInfos[i] = AlarmInfo(alarmInfoName, alarmArray, alarmsJsonArray.size(),
                              daysArray, daysJsonArray.size(), isActive);
    alarmInfos[i].readVals();
  }
  for (int i = 0; i < jsonArray.size(); i++) {
    alarmInfos[i].readVals();
  }
}

This is the output:

Size: 6
12:30:5
13:30:5
14:30:5
15:30:5
16:30:5
17:30:5
Size: 6
17:30:5
17:30:5
18:30:5
19:30:5
20:30:5
21:30:5
Size: 6
17:30:5
17:30:5
18:30:5
19:30:5
20:30:5
21:30:5
Size: 6
17:30:5
17:30:5
18:30:5
19:30:5
20:30:5
21:30:5
Size: 6
17:1073672900:107373
1075862441:825898801
943337780:959984946:
00:1310751:0
20:30:5
1073736416:06:0
Size: 6
17:1073672900:107373
1075862441:825898801
943337780:959984946:
00:1310751:0
20:30:5
1073736416:06:0
Size: 6
17:1073672900:107373
1075862441:825898801
943337780:959984946:
00:1310751:0
20:30:5
1073736416:06:0
Size: 6
17:1073672900:107373
1075862441:825898801
943337780:959984946:
00:1310751:0
20:30:5
1073736416:06:0

to test and debug, missing these includes:

and that file:

/presetdata.json

Duplicate Post:

The libraries are available in Library Manger.
presetdata.json:

[
  {
    "Name": "preset1",
    "Alarms": [
      { "hour": 12, "minute": 30, "duration": 5 },
      { "hour": 13, "minute": 30, "duration": 5 },
      { "hour": 14, "minute": 30, "duration": 5 },
      { "hour": 15, "minute": 30, "duration": 5 },
      { "hour": 16, "minute": 30, "duration": 5 },
      { "hour": 17, "minute": 30, "duration": 5 }
    ],
    "Days": [1, 2, 3, 4],
    "Active": 0
  },
  {
    "Name": "preset2",
    "Alarms": [
      { "hour": 17, "minute": 30, "duration": 5 },
      { "hour": 17, "minute": 30, "duration": 5 },
      { "hour": 18, "minute": 30, "duration": 5 },
      { "hour": 19, "minute": 30, "duration": 5 },
      { "hour": 20, "minute": 30, "duration": 5 },
      { "hour": 21, "minute": 30, "duration": 5 }
    ],
    "Days": [1, 2, 3, 4],
    "Active": 1
  }
]

I think the problem may be in this line. Here an array is passed to the constructor of AlarmInfo, which stores it as a pointer (it does not make a copy). The array however is destroyed once the for-loop iteration ends.

So one solution would be to make the AlarmInfo constructor perform a copy action for the arrays. Alternatively, you could reserve memory for the arrays on the heap by using new and make sure they are deleted properly (using delete) by implementing a destructor (of course this is also needed when a copy is stored).

The first option is a bit more elegant in my opinion, but (temporarily) uses more memory.

@kirca, do not cross-post. Threads merged.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.