Array of struct not working

I am currently working on a small project with a ESP32 (my first project) where I try to build an automation unit for my terrarium. For this I built a unit, which controls daylight, moonlight and rain via RC plug. Now I try to define several schedules (for the timne being two - one for summer month and one for winter). However, I guess I am missing something since the sketch won’t compile.

The idea os my sketch is:

  • define a struct which contains the RC Plug Data (on code, off code, status)
  • define a struct for the switching event (name, reference to the RC Plug, time for switching on, time for switching off, etc.)
  • define a struct for schedules (e.g. winter and summer) which stores an array of switching events.

The RC Plugs are quite static - the on / off codes won’t change once defined. Only the current status can change.

The switchingCycle / switching event should be modifiable (change on / off time, change duration in case of timer based events like the rain pump).

The schedule itself might change, since I plan to add months to it in order to distinguish which schedule should be active at what time of year.

Here a boiled down excerpt of my sketch (I narrowed it down to the structs and assignments):

struct RCPlug {
  const char* switchOnCode;
  const char* switchOffCode;
  bool switchedOn;

  RCPlug(const char* switchOnCode, const char* switchOffCode) : switchOnCode(switchOnCode), switchOffCode(switchOffCode), switchedOn(false)
  {
  }
};

RCPlug dummy = {"",""};

struct SwitchCycleExt {
  const String Name;
  RCPlug& switchPlug;
  int timeHourOn;
  int timeMinuteOn;
  int timeHourOff;
  int timeMinuteOff;
  int timerSeconds;
  bool cycleActive;
  SwitchCycleExt(const String Name, RCPlug& switchPlug, int timeHourOn, int timeMinuteOn, int timeHourOff, int timeMinuteOff, int timerSeconds) : Name(Name), switchPlug(switchPlug), timeHourOn(timeHourOn), timeMinuteOn(timeMinuteOn), timeHourOff(timeHourOff), timeMinuteOff(timeMinuteOff), timerSeconds(timerSeconds), cycleActive(false)
  {
  }
  SwitchCycleExt() : switchPlug(dummy)
  {
  }
};

struct ScheduleExt{
  int idx=0;

  struct SwitchCycleExt switchSchedule[4];
  ScheduleExt() //(int idx, SwitchCycleExt switchSchedule) : idx(idx), switchSchedule({switchSchedule})
  {
  }
};

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  delay(2000); // Warte auf Terminal
  
  SwitchCycleExt c1  = {"Cycle 1", dummy, 99,99,99,99,0};

  ScheduleExt schedule;
  
  schedule.switchSchedule[schedule.idx] = c1;
  schedule.idx++;

  for (int i = 0; i < 4; i++){
    Serial.println(schedule[i].switchSchedule.Name);
  }
}

void loop() {
  // put your main code here, to run repeatedly:
}

Seemingly I made several mistakes: Neither can I add c1 to my array within schedule (error: use of deleted function ‘SwitchCycleExt& SwitchCycleExt::operator=(const SwitchCycleExt&)’
) , nor can loop over the array (error: no match for ‘operator’ (operand types are ‘ScheduleExt’ and ‘int’)).

I am pretty new to Arduiono / ESP32 and I got pretty stuck here. Can anyone give me a hint what I am doing wrong here?

schedule.switchSchedule[schedule.idx] is an object of struct SwitchCycleExt and you are trying to overwrite it’s contents with C1 which is also an object of struct SwitchCycleExt BUT to copy over the content you need to define an assignment operator (the = operator) in struct SwitchCycleExt .

A struct in c++ is the same as a class with, by default, all the members and methods being public, Take a look at C++ Operator Overloading Guidelines to see how to define the assignment operator.

maybe i’m misreading what you’re trying to do. i’ve fond of static initialization of tables (i.e. struct)

you’ve defined a variable, “c1”, as type SwitchCycleExt that is independent of “schedule” defined as type SchedulExt"

shouldn’t there be something like

ScheduleExt schedule = {
       0,     // idx
    { 
          {     }, // first of four definitions of switchSchedule []
          {     }, // 2nd of four definitions of switchSchedule []
          {     }, // 3rd of four definitions of switchSchedule []
          {     }, // 4th of four definitions of switchSchedule []
    },
    ??   // definition of scheduleExt()
};

not sure about that static definition of scheduleExt() and i’m not familiar with static initialization of a class

and “c1” and “schedule” are dynamic w/in setup that won’t be persistent outside of setup.

Hello and thanks for the comment. I also tried the call as you described, but nevertheless it won't work:

ScheduleExt schedule = {0, {{}, {}, {}, {}};

throws the error

expected '}' before ';' token

@skyvan: OK, so the "=" operator has to be defined - I'll check out the link you posted. But why can't I loop over my array?

error: no match for 'operator' (operand types are 'ScheduleExt' and 'int')

ScheduleExt schedule = {0, {{}, {}, {}, {}};

The first thing that I notice, even without having followed the thread, is that the number of left and right curly brackets does not match

Instead of

 struct SwitchCycleExt switchSchedule[4];

you might want to actually keep 4 pointers to your SwitchCycleExt. that would allow you do perform an assignment into the array directly with the address of a relevant instance. of course it means that the instances should not go away as there is no copy made.

may be that example can help:

class A
{
  public:
    A(uint8_t v) : val(v) {}     // constructor
    uint8_t getValue() {
      return val;
    }

    uint8_t val; // could be private
};

class B
{
  public:
    A* list[4];
};

A a0(0);
A a1(11);
A a2(22);
A a3(33);

B records;

void setup()
{
  Serial.begin(115200);
  records.list[0] = &a0;
  records.list[1] = &a1;
  records.list[2] = &a2;
  records.list[3] = &a3;

  for (byte i = 0; i < 4; i++) {
    Serial.print(F("value at index "));
    Serial.print(i);
    Serial.print(F(" = "));
    Serial.println(records.list[i]->getValue());
  }
}

void loop() {}

or if things are to be statically defined, you can also initialize the record's instance array right during it’s definition since the A instances are also known at that point.

class A
{
  public:
    A(uint8_t v) : val(v) {}     // constructor
    uint8_t getValue() {
      return val;
    }

    uint8_t val; // could be private
};

class B
{
  public:
    A* list[4];
};

A a0(0);
A a1(11);
A a2(22);
A a3(33);

B records = {&a0, &a1, &a2, &a3};

void setup()
{
  Serial.begin(115200);
  for (byte i = 0; i < 4; i++) {
    Serial.print(F("value at index "));
    Serial.print(i);
    Serial.print(F(" = "));
    Serial.println(records.list[i]->getValue());
  }
}

void loop() {}

in both cases you should see in the Serial Monitor (@ 115200 bauds)

[color=purple]
value at index 0 = 0
value at index 1 = 11
value at index 2 = 22
value at index 3 = 33
[/color]

if the B class is just to hold the array and there is no smart function members associated, then you could just keep an array of pointers to A as a global variable, the class has no real value added.

So using pointers would solve the problem? I'm not that familiar with pointers, but as far as I remember I would require one instances of the struct per pointer, right?
In my code I defined c1, c2, ... just because I didn't manage to define the array. The idea is to store the switch cycles only in the array. Which brings us back to the idea with the pointers: if I define a second schedule, I would require 8 objects of type switchCycleExt, 12 if I have 3 schedules and so on... right?

A pointer is just the address of the Struct / Class in memory. if you want to record it, it needs to exists somewhere.

look at the code above. this is defining the 4 instances (of Class A) and allocating memory for the instance variables (here just a byte as an example)

A a0(0);
A a1(11);
A a2(22);
A a3(33);

and this is how you can initialize an instance of the other Struct / Class (B) with the pointers to the instances you just created

B records = {&a0, &a1, &a2, &a3};

if you have different objets (instances) then you need to create them so that they exist in memory and then play with their pointer. In the code above, for example Serial.println(records.list[i]->getValue()); records is the instance of the class B, so records.list is its instance variable, it's an array of pointers to instances of type A, so if you want to get the 3rd one you would records.list[2]. Since this is a pointer to an object of class A, to invoke a particular method you use the -> notation and this for example records.list[2]->getValue() would trigger the getValue() method of that instance.