accessing struct elements with a for loop

Greetings;

My pressure monitoring system needs a calibration routine, which allows me to input 8 variables (byte and int, maybe a char in the future) and then save those to EEPROM. All Together, I'll need 4 of these, for 4 pressure sensors.

I built a struct:

// This menu is to display and calibrate the EZO stamps.
struct pSensor {
  byte  pId = 5;         //identifies Analog Channel (min channel 5)------not in PRESSURE
  byte  pSamples = 1;    //number of samples to filter reading (default 1)
  int   pCalZero = 0;    //sensor reading with no water column
  int   pCalHigh = 0;    //sensor reading with max water column
  int   hRefZero = 0;    //actual NO watercolumn height of sensor in CM (default is 0)
  int   hRefHigh = 200;    //actual HIGH watercolumn height in CM (default 200)
  int   unitAdjust = 0;  // adjust units to display VOLUME on screen (default 0-> bars, 1->CM, other m3))
  int   tankVol = 0;    // full tank volume in LITRES (default 0)
};
pSensor displayDefault;

and initialized it.

the default values (and future calibration values) need to be displayed in a table on my tft screen. Currently, I'm using the following routine for this:

 //paint the data boxes for the Sensor Array
  for (uint8_t row = 0; row < 4; row++) {
    for (uint8_t col = 0; col < 2; col++) {
      tft.drawRect(ABOX_X + col * (ABOX_W + ASPACING_X), ABOX_Y + row * (ABOX_H + ASPACING_Y), ABOX_W, ABOX_H, ILI9341_WHITE); // create 'text field'
      tft.setCursor(ABOX_X + 1 + col * (ABOX_W + ASPACING_X), ABOX_Y - 15 + row * (ABOX_H + ASPACING_Y));
      tft.setTextSize(2);
      tft.print(boxlabels[(row*2)+col]);
      //set cursor location and text size
      //tft.print(row and column based access of displayDefault);
    }
  }

Is there a way, in which I can address the elements in the structure with an expression like

displayDefault.[row * 2 + col], so I can cycle through them easily (last line in for loop)?

If there is an other data type than structure, which would be more apt for the application (EEPROM) and easier to manipulate, that would be ok too...

Cheers;

You mean an array of structs?

pSensor displayDefaults[size];
...
something = displayDefaults[row * 2 + col].pCalHigh;

?

trilife:
Is there a way, in which I can address the elements in the structure with an expression like

displayDefault.[row * 2 + col], so I can cycle through them easily (last line in for loop)?

Not easily. You could set up a pointer to the start of the struct and increment the address of the pointer for each item. I think you will have to figure out how to get the pointer to recognise different datatypes which can be PITA

Iteration is much easier with an array but then you loose the convenience of the individual names (there is a work around for that) and all the items in an array must be the same datatype.

If you have an array (for example)

int myArrray[4] = {12,23,36,18};

you can iterate over that with a FOR loop.

And you could create a series of variables that allow you to access the elements with names - like this

byte pCalZero = 0; // 0 is the first element of the array
byte pCalHigh = 1;
// etc

and then access the elements like this

myHighValue = myArray[pCalHigh];

...R

Thanks @aarg and @Robin2;

I think I'll go with the solution of an int Array[8]. I have only 4 of these in my project and 1k of EEPROM on my MEGA2560... not worth struggling with structs, if we're only saving 8 bytes all together.

One quick question though

byte  a = 0; // 0 is the first element of the array
const byte b =1;
#define c  2
// etc

what is the difference between byte ..., const byte ... and #define... in the code above?[/code]

Cheers;

Why not just waste 2 bytes per struct and make each of the values ints then you can treat them as structs when you want the convenience of named elements and as arrays when you want the convenience of arrays that you can iterate through

UKHeliBob:
Why not just waste 2 bytes per struct and make each of the values ints then you can treat them as structs when you want the convenience of named elements and as arrays when you want the convenience of arrays that you can iterate through

That sound really good!

I can call displayDefault.tankVol

now, how do I go about "iterating through"? displayDefault.[?]

I've changed the first two elements to unsigned ints as bytes are unsigned as well.

// This menu is to display and calibrate the EZO stamps.
struct pSensor {
  uint16_t pId = 5;         //identifies Analog Channel (min channel 5)------not in PRESSURE
  uint16_t pSamples = 1;    //number of samples to filter reading (default 1)
  int      pCalZero = 0;    //sensor reading with no water column
  int      pCalHigh = 0;    //sensor reading with max water column
  int      hRefZero = 0;    //actual NO watercolumn height of sensor in CM (default is 0)
  int      hRefHigh = 200;    //actual HIGH watercolumn height in CM (default 200)
  int      unitAdjust = 0;  // adjust units to display VOLUME on screen (default 0-> bars, 1->CM, other m3))
  int      tankVol = 0;    // full tank volume in LITRES (default 0)
};
pSensor displayDefault;


void setup()
{
  Serial.begin(57600);

  // pointer to array of 16 bit values
  int *p;
  // take address of displayDefault and assign to the pointer
  p = (int*)&displayDefault;

  // loop thorugh the elements of the struct
  for (uint8_t cnt = 0; cnt < sizeof(displayDefault) / sizeof(int); cnt++)
  {
    // p points to an address of an element in the array; *p gets you the value ofthat address
    // print it and next point the pointer to the address of the next element
    Serial.println(*(p++));
  }
}

void loop()
{
}

You can also use below print instead of the print I used; easier to understand, but I (usually) forget about that :frowning:

    Serial.println(p[cnt]);

If you want to have both "select by index" (array) and "select by name" (struct) I would go with an array and simulate the "select by name" with an enum:

// This menu is to display and calibrate the EZO stamps.
int pSensor[] =
{
  5, //  pId        //identifies Analog Channel (min channel 5)------not in PRESSURE
  1, //  pSamples  //number of samples to filter reading (default 1)
  0, //   pCalZero  //sensor reading with no water column
  0, //   pCalHigh  //sensor reading with max water column
  0. //   hRefZero   //actual NO watercolumn height of sensor in CM (default is 0)
  200, //   hRefHigh   //actual HIGH watercolumn height in CM (default 200)
  0, // int   unitAdjust  // adjust units to display VOLUME on screen (default 0-> bars, 1->CM, other m3))
  0  // int   tankVol    // full tank volume in LITRES (default 0)
};

pSensor displayDefault;

enum {pId, pSamples, pCalZero, pCalHigh, hRefZero, hRefHigh, unitAdjust, tankVol};

Now you can use displayDefault[index] for "select by index" and displayDefault[name] (in place of displayDefault.name) to select by name.

This is what I had in mind

union unionDef
{
  struct structDef
  {
    int intA;
    int intB;
  } aStruct;
  int anArray[2];
} aUnion;

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  aUnion.anArray[0] = 123; //put values in the array
  aUnion.anArray[1] = 456;
  Serial.println(aUnion.aStruct.intA);  //and read them from the struct
  Serial.println(aUnion.aStruct.intB);
  Serial.println();
  aUnion.aStruct.intA = 1234; //or put values in the struct
  aUnion.aStruct.intB = 5678;
  for (int x = 0; x < 2; x++) //and read them from the array
  {
    Serial.println(aUnion.anArray[x]);
  }
}

void loop()
{
}

As you can see you can refer to the values using either the array or the struct and iterate through the array when it suits you

sterretje:
I've changed the first two elements to unsigned ints as bytes are unsigned as well.

// This menu is to display and calibrate the EZO stamps.

struct pSensor {
 uint16_t pId = 5;         //identifies Analog Channel (min channel 5)------not in PRESSURE
 uint16_t pSamples = 1;    //number of samples to filter reading (default 1)
 int      pCalZero = 0;    //sensor reading with no water column
 int      pCalHigh = 0;    //sensor reading with max water column
 int      hRefZero = 0;    //actual NO watercolumn height of sensor in CM (default is 0)
 int      hRefHigh = 200;    //actual HIGH watercolumn height in CM (default 200)
 int      unitAdjust = 0;  // adjust units to display VOLUME on screen (default 0-> bars, 1->CM, other m3))
 int      tankVol = 0;    // full tank volume in LITRES (default 0)
};
pSensor displayDefault;

void setup()
{
 Serial.begin(57600);

// pointer to array of 16 bit values
 int p;
 // take address of displayDefault and assign to the pointer
 p = (int
)&displayDefault;

// loop thorugh the elements of the struct
 for (uint8_t cnt = 0; cnt < sizeof(displayDefault) / sizeof(int); cnt++)
 {
   // p points to an address of an element in the array; p gets you the value ofthat address
   // print it and next point the pointer to the address of the next element
   Serial.println(
(p++));
 }
}

void loop()
{
}



You can also use below print instead of the print I used; easier to understand, but I (usually) forget about that :(


Serial.println(p[cnt]);

Thanks, So I tested both of your print statements,

// This menu is to display and calibrate the EZO stamps.
struct pSensor {
  uint16_t pId = 105;         //identifies Analog Channel (min channel 5)------not in PRESSURE
  uint16_t pSamples = 1;    //number of samples to filter reading (default 1)
  int      pCalZero = 2;    //sensor reading with no water column
  int      pCalHigh = 3;    //sensor reading with max water column
  int      hRefZero = 4;    //actual NO watercolumn height of sensor in CM (default is 0)
  int      hRefHigh = 200;    //actual HIGH watercolumn height in CM (default 200)
  int      unitAdjust = 5;  // adjust units to display VOLUME on screen (default 0-> bars, 1->CM, other m3))
  int      tankVol = 6;    // full tank volume in LITRES (default 0)
};
pSensor displayDefault;


void setup()
{
  Serial.begin(115200);
  Serial.println("start structTest");
  Serial.println(sizeof(displayDefault));
  Serial.println(sizeof(int));
  // pointer to array of 16 bit values
  int *p;
  // take address of displayDefault and assign to the pointer
  p = (int*)&displayDefault;

  // loop thorugh the elements of the struct
  for (uint8_t cnt = 0; cnt < sizeof(displayDefault) / sizeof(int); cnt++)
  {
    // p points to an address of an element in the array; *p gets you the value ofthat address
    // print it and next point the pointer to the address of the next element
    Serial.print(cnt);
        Serial.print(":");
    Serial.print(p[cnt]);
        Serial.print(":");
    Serial.println(*(p++));
  }
}

void loop()
{
}

.

print(p[cnt]); seems to return unexpected stuff.

print(p[cnt]); seems to return unexpected stuff.

Why is that unexpected?

TheMemberFormerlyKnownAsAWOL:
Why is that unexpected?

The sketch returns:

7:09:53.692 -> start structTest
17:09:53.692 -> 16
17:09:53.692 -> 2
17:09:53.692 -> 0:105:105
17:09:53.692 -> 1:2:1
17:09:53.692 -> 2:4:2
17:09:53.692 -> 3:5:3
17:09:53.692 -> 4:0:4
17:09:53.692 -> 5:311:200
17:09:53.692 -> 6:207:5
17:09:53.692 -> 7:256:6

.

the print(p[ctn]); is the column between the ":"s. it should read the same as the one after the last ":".

I'm still unsure why this is unexpected.

What is expected?

sterretje in post #6 stated that the two print statements should retrieve the same contents of the struct.

So, I included both variations in the same sketch, back to back.
They don't produce the same result...

UKHeliBob:
This is what I had in mind

union unionDef

{
  struct structDef
  {
    int intA;
    int intB;
  } aStruct;
  int anArray[2];
} aUnion;

void setup()
{
  Serial.begin(115200);
  while (!Serial);
  aUnion.anArray[0] = 123; //put values in the array
  aUnion.anArray[1] = 456;
  Serial.println(aUnion.aStruct.intA);  //and read them from the struct
  Serial.println(aUnion.aStruct.intB);
  Serial.println();
  aUnion.aStruct.intA = 1234; //or put values in the struct
  aUnion.aStruct.intB = 5678;
  for (int x = 0; x < 2; x++) //and read them from the array
  {
    Serial.println(aUnion.anArray[x]);
  }
}

void loop()
{
}



As you can see you can refer to the values using either the array or the struct and iterate through the array when it suits you

Thanks Bob, this is too complex for my little brain. I saved it as a sketch to explore, once my skill level increases. For now, I'm either going with a sterretje's #6 or JohnWasser's #7...

I'll change this post to [SOLVED]. I'm sure you'll hear me begging for more help sooner than later.

Big thanks from a - still - locked down Colombia

    Serial.print(p[cnt]);
    Serial.println(*(p++));

The problem is that the second print changes the value of 'p' so it no longer points to the first element of the array as assumed in the first print. You can use one or the other, but not both on the same pointer.

This should produce the expected results:

// pointer to array of 16 bit values
  int *p1, *p2;
  // take address of displayDefault and assign to the pointer
  p1 = (int*)&displayDefault;
  p2 =  (int*)&displayDefault;

  // loop thorugh the elements of the struct
  for (uint8_t cnt = 0; cnt < sizeof(displayDefault) / sizeof(int); cnt++)
  {
    // p points to an address of an element in the array; *p gets you the value ofthat address
    // print it and next point the pointer to the address of the next element
    Serial.print(cnt);
        Serial.print(":");
    Serial.print(p1[cnt]);
        Serial.print(":");
    Serial.println(*(p2++));
  }
}

got it! it works.

Thanks.

[SOLVED]