Coding a CLASS and passing variables [SOLVED]

Greetings;

I am building this pump room monitor, which has a need to read 4 pressure sensors, filtering their outputs, mapping them, constraining them, conditioning them for displaying etc.

Here is the portion of the code, which does this for one of the sensors.

//Read and convert Tank Pressure
  float samples = 100;
  int tankZero = 86;
  int tank200 = 289;
  int volZero = 0;
  int vol200 = 25650;
  long int sensorValue7 = 0;
  int analogChannel = 7;
  for (int sampleCount = 0; sampleCount < samples; sampleCount++) {
    sensorValue7 = sensorValue7 + analogRead(analogChannel);
    delay (1);
  }
  pressureValue7 = float(sensorValue7) / samples; 
  pressureValue7 = map(pressureValue7, tankZero, tank200, volZero, vol200);
  pressureValue7 = constrain(pressureValue7, volZero, vol200);
  pressureValue7 = pressureValue7 / 1000;
  
  //Check to see, if Email needs to be sent
  if (pressureValue7 < 2.0){
    //display Value on TFT
    displayValue(oldValue7, pressureValue7,RIGHTMARGIN +RAINOFFSET, TOPMARGIN, RED, 2); //Print to TFT
    oldValue7 = pressureValue7; //Set Value to Clean Screen
   // sendEmail(pressureValue7); //------------------------------temporarily commented out to avoid emails
  }
  if (pressureValue7 >5.0) {           //reset email Flag ----------------> needs more conditions
    displayValue(oldValue7, pressureValue7,RIGHTMARGIN + RAINOFFSET, TOPMARGIN, GREEN, 2); //Print to TFT
    oldValue7 = pressureValue7; //Set Value to Clean Screen
    needToSendEmail = true;
  }
  
  //display Value on TFT
  displayValue(oldValue7, pressureValue7,RIGHTMARGIN + RAINOFFSET, TOPMARGIN, YELLOW, 2); //Print to TFT
  oldValue7 = pressureValue7; //Set Value to Clean Screen

Reading up on the tutorials, it seems like writing a class for this works, rather then repeating the whole thing umpteen times seems the right way to go.

Except, I have a hard time accepting that I have to pass oodles of variables everytime, when I really only need to know the analog channel. The other variables (sample, tankZero, volZero, all the boundary conditions) are all unique to that one channel number passed.

I'm guessing there is some sort of array[], which would solve this, but am unsure on how to start this one.

As an expansion of this topic, at one point, I will need to also include a routine to "calibrate" these pressure sensors, i.e. set their respective tankZero and tank200 values... Any help there would be appreciated as well, although lower priority.

Cheers,

Four <> umpteen.

Making a class or library would make the code cleaner, but what is the problem with replicating code if it's only four times?

I have little experience with classes- I wrote a library once from a tutorial just to learn about writing a library, so I don't think I can help you with putting your repetitious code into a class.

Hi trilife,

If you're not confident with arrays, then classes and objects are a little beyond your experience level at the moment. And probably unnecessary in order to make your code short and easy to read & understand. Get it working by copy/pasting sections as needed, then post it here and we can suggest ways to shrink & tidy it. Please read the forum guide before you post your code!

You can build a struct as a container for all variables related to one sensor. Then build an array of that struct, to hold the variables of all sensors. Then you can write functions that receive a reference to a sensor struct and do whatever they are supposed to do. Or you extend the struct into a class by adding the functions as member methods, but that does not add any functionality, only saves a bit of writing.

Maybe start with a struct. You can define one that contains the variables for each sensor. Perhaps make four instances and call your functions passing one of them at a time.

Later convert to an array of structs.

Then, since a struct is very similar to a class, make the solution an array of objects.

Ninja'd by DrDiettrich.

PaulRB:
Hi trilife,

If you're not confident with arrays, then classes and objects are a little beyond your experience level at the moment. And probably unnecessary in order to make your code short and easy to read & understand. Get it working by copy/pasting sections as needed, then post it here and we can suggest ways to shrink & tidy it. Please read the forum guide before you post your code!

Thanks PaulRB, this seems like a fair assessment of my capabilities at the moment and I will proceed as suggested.

As to your final sentence about reading the forum guide, I had done it before I posted and reread it again just now. I clearly must be missing something... I would welcome a clarification.

Cheers.

Two guys same thoughts - oh boy are we good! :wink:

DrDiettrich:
You can build a struct as a container for all variables related to one sensor. Then build an array of that struct, to hold the variables of all sensors. Then you can write functions that receive a reference to a sensor struct and do whatever they are supposed to do. Or you extend the struct into a class by adding the functions as member methods, but that does not add any functionality, only saves a bit of writing.

Thanks. the Struct part sounds interesting. Just read the tutorial. I'll play with that too. Lots of Lockdown time on my hands right now.

Maybe combine this with PaulRB's suggestion on the same topic.

trilife:
..reread it again just now. I clearly must be missing something... I would welcome a clarification.

Item #7. If you had trouble finding it, that's feedback the forum moderators should hear.

Ok, so I am trying to do this with struct...

I pared down the code to a very simple version.

I manage to serial.print the contents of the struct and an analogRead alright, but as soon as I try to call the function, it refuses to compile. Sure to have a total rookie mistake here...

//Structures for Pressure sensors
byte rainTank = 7;
byte poolFilt = 8; 
struct Pressure {
  byte analogChannel;
  float samples;
  int calZero;
  int cal200;
  int volZero;
  int vol200;
  int unitAdjust;
};

//Variables and Constants for WATER TANK
Pressure rainWater = {rainTank, 100, 86, 289, 0, 25560, 1000};
//Variables and Constants for POOL FILTER
Pressure poolFilter = {poolFilt, 100, 50, 250, 0, 800, 1};

float pressureValue7 = 0;         //Water Tank sensor
float oldValue7 = pressureValue7; //used to clean the screen


#define RAINAREA  128250  // Rain tank = 570 x 225cm = 128,250 cm2 = 12.825 m2
#define TANKMAX   190     //190cm height from sensor to lip of window that already should trigger an alarm,
                          //the overflow lies below that.
#define RAINSENSORHEIGHT  9 //height of sensor from bottom of tank. reference only                          
#define MAXVOLRAIN  (RAINAREA * TANKMAX)  

//Global Variables for Conductivity Sensor
float oldEC;  //old value to refresh screen

//-------------------SETUP STARTS HERE-------------------------

void setup(void) {
  Serial.begin(115200);
  Serial.println(analogRead(rainWater.analogChannel));

  
}

//-------------------LOOP STARTS HERE----------------------

void loop(void) {
  Serial.print(rainWater.analogChannel);
  Serial.print("  ");
  Serial.println(analogRead(rainWater.analogChannel));

  Serial.print(poolFilter.analogChannel);
  Serial.print("  ");
  Serial.println(analogRead(poolFilter.analogChannel));


  //Read and convert Tank Pressure
  Serial.print("from function:");
  Serial.print(rainWater.analogChannel);
  Serial.println(readPressure(rainTank));

  //Read and convert Tank Pressure
  Serial.print("from function:");
  Serial.print(poolFilter.analogChannel);
  Serial.println(readPressure(poolFilt));

  
//  pressureValue7 = readPressure(rainWater);

  delay(1000);  //-----------------------Change to Millis()

}

//read Pressure
float readPressure(byte myAnalogChannel){

  long int sensorValue7 = 0;
  for (int sampleCount = 0; sampleCount < Pressure.samples; sampleCount++) {
    sensorValue7 = sensorValue7 + analogRead(myAnalogChannel);
    delay (1);
  }
  pressureValue7 = float(sensorValue7) / Pressure.samples; 
  pressureValue7 = map(pressureValue7, Pressure.tankZero, Pressure.tank200, Pressure.volZero, Pressure.vol200);
  pressureValue7 = constrain(pressureValue7, Pressure.volZero, Pressure.vol200);
  pressureValue7 = pressureValue7 / Pressure.unitAdjust;
  return pressureValue7;
}

The compile error I get is:

C:\Users\JRStrele\Desktop\Arduino Sketches Orientes\Pump Room STRUCT\Pump_Room_STRUCT\Pump_Room_STRUCT.ino: In function 'float readPressure(byte)':

Pump_Room_STRUCT:74:51: error: expected primary-expression before '.' token

   for (int sampleCount = 0; sampleCount < Pressure.samples; sampleCount++) {

                                                   ^

exit status 1
expected primary-expression before '.' token

Cheers;

@PaulRB, I tried to follow step#7 verbatim this time. Before that, I used to hit </> first and copy the code between the code brackets. This seemed to show up correctly in preview. This time I copied the code first, highlighted it and clicked </>. Was this the problem? Cheers.

Your posting of code is fine, thanks.

for (int sampleCount = 0; sampleCount < Pressure.samples; sampleCount++) {

"Pressure" is the name of the struct type. At run-time, it doesn't exist as a variable you can access, only the variables you declare of that type exist and can be accessed. You have declared two: "rainWater" and "poolFilter". Which one is the function referring to? How does the function know which to refer to?

ACK, an instance (variable) name is required for access, not the type name.

Isn't "Pressure" only the tag name, for use as "struct Pressure"?

Case in point, PaulRB was correct in his original answer to this post that arrays, classes and objects are beyond my current capabilities!! good eye Paul!

What I'm trying to do:

  • I have several analog pressure sensors, reading different parts of my system
  • they all have certain parameters I need to use to condition the signal: sample size for averaging, calibration hi/lo points, alarm conditions.
  • some of these parameters are fixed, others will need to be saved into EEPROM when first installed (currently set during compile), but then stay put.

after these pressures have been read and conditioned with those parameters, they get displayed, or emailed.

Currently, within my "scope of experience", I have to run 4 segments of code in the main loop, do the conditioning once for every pressure transducer... This is working well, but I'm afraid that once I start calibrating and saving stuff into EEPROM, this code turns into spaghetti, since I will have to do everything 4 time (or more, as the project evolves).

This is why I was trying to set up a set of parameters associated with an analog Input (array, struct, tuple, whatever), which I can pass to a certain function for processing.

As I'm writing this, a potential solution comes to mind:

I can define one the Pressure Structure, declare 4 of them, rainWater, poolFilter, etc.

the function float readPressure(analogInput) will have a series of if statments, testing analogInput, and use the according data in the rainWater, poolFilter etc structures to return a conditioned reading.

This should work.

What I was trying to do though, was to pass the entire instance of Pressure say rainWater to the function and have it do the conditioning.

//Structures for Pressure sensors
byte rainTank = 7;
byte poolFilt = 8;
struct Pressure {
  byte analogChannel;
  float samples;
  int calZero;
  int cal200;
  int volZero;
  int vol200;
  int unitAdjust;
};

//Variables and Constants for WATER TANK
Pressure rainWater = {rainTank, 100, 86, 289, 0, 25560, 1000};
//Variables and Constants for POOL FILTER
Pressure poolFilter = {poolFilt, 100, 50, 250, 0, 800, 1};
//Instance for the readPressure function
Pressure myConditioningStructure;

float pressureValue7 = 0;         //Water Tank sensor
float oldValue7 = pressureValue7; //used to clean the screen


#define RAINAREA  128250  // Rain tank = 570 x 225cm = 128,250 cm2 = 12.825 m2
#define TANKMAX   190     //190cm height from sensor to lip of window that already should trigger an alarm,
                          //the overflow lies below that.
#define RAINSENSORHEIGHT  9 //height of sensor from bottom of tank. reference only                         
#define MAXVOLRAIN  (RAINAREA * TANKMAX) 

//Global Variables for Conductivity Sensor
float oldEC;  //old value to refresh screen

//-------------------SETUP STARTS HERE-------------------------

void setup(void) {
  Serial.begin(115200);
  Serial.println(analogRead(rainWater.analogChannel));

 
}
//read Pressure
float readPressure(struct myConditioningStructure){

  long int sensorValue7 = 0;
  for (int sampleCount = 0; sampleCount < myConditioningStructure.samples; sampleCount++) {
    sensorValue7 = sensorValue7 + analogRead(myAnalogChannel);
    delay (1);
  }
  pressureValue7 = float(sensorValue7) / myConditioningStructure.samples;
  //pressureValue7 = map(pressureValue7, Pressure.tankZero, Pressure.tank200, Pressure.volZero, Pressure.vol200);
 
  pressureValue7 = pressureValue7 / myConditionindStructure.unitAdjust;
  return pressureValue7;
}

In essence, how do I pass the data inside the rainWater instance Pressure to the myConditioningStructure?

Stumped!

Thanks for bearing with me guys. 40 years ago, I was doing assembler programming for the COP800. things have changed!!!

You don't want to use a myConditioningStructure, it's not required nor helpful. Instead you pass to the function just the right (e.g. rainWater) instance, so that all its variables are known to the function.

trilife:
Case in point, PaulRB was correct in his original answer to this post that arrays, classes and objects are beyond my current capabilities!! good eye Paul!

What I'm trying to do:

  • I have several analog pressure sensors, reading different parts of my system
  • they all have certain parameters I need to use to condition the signal: sample size for averaging, calibration hi/lo points, alarm conditions.
  • some of these parameters are fixed, others will need to be saved into EEPROM when first installed (currently set during compile), but then stay put.

after these pressures have been read and conditioned with those parameters, they get displayed, or emailed.

Currently, within my "scope of experience", I have to run 4 segments of code in the main loop, do the conditioning once for every pressure transducer... This is working well, but I'm afraid that once I start calibrating and saving stuff into EEPROM, this code turns into spaghetti, since I will have to do everything 4 time (or more, as the project evolves).

This is why I was trying to set up a set of parameters associated with an analog Input (array, struct, tuple, whatever), which I can pass to a certain function for processing.

As I'm writing this, a potential solution comes to mind:

I can define one the Pressure Structure, declare 4 of them, rainWater, poolFilter, etc.

the function float readPressure(analogInput) will have a series of if statments, testing analogInput, and use the according data in the rainWater, poolFilter etc structures to return a conditioned reading.

This should work.

What I was trying to do though, was to pass the entire instance of Pressure say rainWater to the function and have it do the conditioning.

//Structures for Pressure sensors

byte rainTank = 7;
byte poolFilt = 8;
struct Pressure {
 byte analogChannel;
 float samples;
 int calZero;
 int cal200;
 int volZero;
 int vol200;
 int unitAdjust;
};

//Variables and Constants for WATER TANK
Pressure rainWater = {rainTank, 100, 86, 289, 0, 25560, 1000};
//Variables and Constants for POOL FILTER
Pressure poolFilter = {poolFilt, 100, 50, 250, 0, 800, 1};
//Instance for the readPressure function
Pressure myConditioningStructure;

float pressureValue7 = 0;         //Water Tank sensor
float oldValue7 = pressureValue7; //used to clean the screen

#define RAINAREA  128250  // Rain tank = 570 x 225cm = 128,250 cm2 = 12.825 m2
#define TANKMAX   190     //190cm height from sensor to lip of window that already should trigger an alarm,
                         //the overflow lies below that.
#define RAINSENSORHEIGHT  9 //height of sensor from bottom of tank. reference only                        
#define MAXVOLRAIN  (RAINAREA * TANKMAX)

//Global Variables for Conductivity Sensor
float oldEC;  //old value to refresh screen

//-------------------SETUP STARTS HERE-------------------------

void setup(void) {
 Serial.begin(115200);
 Serial.println(analogRead(rainWater.analogChannel));

}






//read Pressure
float readPressure(struct myConditioningStructure){

long int sensorValue7 = 0;
 for (int sampleCount = 0; sampleCount < myConditioningStructure.samples; sampleCount++) {
   sensorValue7 = sensorValue7 + analogRead(myAnalogChannel);
   delay (1);
 }
 pressureValue7 = float(sensorValue7) / myConditioningStructure.samples;
 //pressureValue7 = map(pressureValue7, Pressure.tankZero, Pressure.tank200, Pressure.volZero, Pressure.vol200);

pressureValue7 = pressureValue7 / myConditionindStructure.unitAdjust;
 return pressureValue7;
}




In essence, how do I pass the data inside the rainWater instance Pressure to the myConditioningStructure?

Stumped!

Thanks for bearing with me guys. 40 years ago, I was doing assembler programming for the COP800. things have changed!!!

Guys, Ignore the above!! It must have been late last night. This morning, after several cups of coffee, I figured it out, I think. I declared an instance of myConditioningStructure and discovered some typos. It now compiles and returns data...

//Structures for Pressure sensors
byte rainTank = 7;
byte poolFilt = 8; 
struct Pressure {
  byte analogChannel;
  float samples;
  int calZero;
  int cal200;
  int volZero;
  int vol200;
  int unitAdjust;
};

//Variables and Constants for WATER TANK
Pressure rainWater = {rainTank, 100, 86, 289, 0, 25560, 1000};
//Variables and Constants for POOL FILTER
Pressure poolFilter = {poolFilt, 100, 50, 250, 0, 800, 1};
//
Pressure myConditioningStructure = {0,0,0,0,0,0,0};

float pressureValue7 = 0;         //Water Tank sensor
float oldValue7 = pressureValue7; //used to clean the screen


#define RAINAREA  128250  // Rain tank = 570 x 225cm = 128,250 cm2 = 12.825 m2
#define TANKMAX   190     //190cm height from sensor to lip of window that already should trigger an alarm,
                          //the overflow lies below that.
#define RAINSENSORHEIGHT  9 //height of sensor from bottom of tank. reference only                          
#define MAXVOLRAIN  (RAINAREA * TANKMAX)  

//Global Variables for Conductivity Sensor
float oldEC;  //old value to refresh screen

//-------------------SETUP STARTS HERE-------------------------

void setup(void) {
  Serial.begin(115200);
  Serial.println(analogRead(rainWater.analogChannel));

  
}

//-------------------LOOP STARTS HERE----------------------

void loop(void) {
  Serial.print(rainWater.analogChannel);
  Serial.print("  ");
  Serial.println(analogRead(rainWater.analogChannel));

  Serial.print(poolFilter.analogChannel);
  Serial.print("  ");
  Serial.println(analogRead(poolFilter.analogChannel));


  //Read and convert Tank Pressure
  Serial.print("from function");
  Serial.print(rainWater.analogChannel);
  Serial.print("  :");
  Serial.println(readPressure(rainWater));

  //Read and convert Tank Pressure
  Serial.print("from function");
  Serial.print(poolFilter.analogChannel);
  Serial.print("  :");
  Serial.println(readPressure(poolFilter));

  
//  pressureValue7 = readPressure(rainWater);

  delay(1000);  //-----------------------Change to Millis()

}

//read Pressure
float readPressure(Pressure myConditioningStructure){

  long int sensorValue7 = 0;
  for (int sampleCount = 0; sampleCount < myConditioningStructure.samples; sampleCount++) {
    sensorValue7 = sensorValue7 + analogRead(myConditioningStructure.analogChannel);
    delay (1);
  }
  pressureValue7 = float(sensorValue7) / myConditioningStructure.samples; 
  pressureValue7 = map(pressureValue7, myConditioningStructure.calZero, myConditioningStructure.cal200, myConditioningStructure.volZero, myConditioningStructure.vol200);
  pressureValue7 = constrain(pressureValue7, myConditioningStructure.volZero, myConditioningStructure.vol200);
  pressureValue7 = pressureValue7 / myConditioningStructure.unitAdjust;
  return pressureValue7;
}

thanks for your help. Case closed

DrDiettrich:
You don't want to use a myConditioningStructure, it's not required nor helpful. Instead you pass to the function just the right (e.g. rainWater) instance, so that all its variables are known to the function.

Hello DrDiettrich;

Ok, how do call the individual elements of the structure within the function then?

From the main loop, I call readPressure(rainWater); or readPressure(poolFilter); how do I use say the second element in the structure? it would be rainWater.samples or poolFilter.samples...

It's fine as you have it. But there is no need to declare myConditioningStructure on line 22. It is a parameter of the readPressure function which you are using correctly, if inefficiently (a reference might be better).

Declaring it earlier just allocated memory that is never used.

trilife:
Ok, how do call the individual elements of the structure within the function then?

You name the function parameter theStruct and refer to the struct elements by theStruct.theElement.

float readPressure(struct Pressure &theStruct);

wildbill, DrDiettrich, PaulRB, thanks for your help.

I made the change with the &thePressure, which I suppose is "the reference" wildbill is referring to. Compiles and works as advertised.

This will vastly streamline my programming. The plan is to create a data structure for each measurement and add to it such elements as the input conditioning already discussed, as well as locations/colors on the TFT/webpage etc.

That way, I can just pass the same structure to various read/print/publish and have these functions pick the elements they need...

Thanks again.

now all I need to figure out is how to add [SOLVED] to the title of this post.