Accessing all instances of a class at once

Hello :slight_smile: I am back with a mighty question :grimacing:.... or maybe not...

I am re-writing a class to read potentiometers. It works well, but I want to take it a step further by removing all the Serial.print bits in the loop section of the sketch.
Basically I would like to achieve the same result with just one line in the main code. I am not sure if this can be achieved. To do this I was hoping to create a static function in the class so that when I call this function, it prints out the values of a specific variable (or return value of a function) for each instance of the class (for all of instances to be clear). In this case the return value of the function readValue() for each object.

I am struggling with the fact that I am attempting to get the value without naming the object when I do that so I get the expected error ...

I will show it below:

#define DEBUG
#include "Potentiometer.h"

#define POT_NUM 6                                                   // number of potentiometers

Potentiometer Pot[POT_NUM] = {(A0), (A1), (A2), (A3), (A4), (A5)};  // creates a number (POT_NUM) of objects of Potentiometer class and passes the pin number as argument

int potReading [POT_NUM];                                           // variable to store  the returned potentiometers reading values 

//**********************************************************************************************************************************************

void setup() {

#ifdef DEBUG  
    Serial.begin(9600);
    Serial.print("Number of pots: ");
    Serial.println(Potentiometer::getCounter()); 

#endif  

}

//**********************************************************************************************************************************************

void loop() {

  for(int i = 0 ; i < POT_NUM; i ++){                                // read the pot values 
    potReading [i] = Pot[i].readValue();
  }

#ifdef DEBUG

  for(int i = 0 ; i < POT_NUM; i ++){                                // this is the part I want to replace 
      Serial.print("Pot n");
      Serial.print(i + 1); 
      Serial.print("\t");                                          
      Serial.print(potReading [i]);
      Serial.print("\t"); 
      if (i == POT_NUM - 1){Serial.println();} 
  }

#endif
  
}

this is the header file:

#ifndef POTENTIOMETER_H
#define POTENTIOMETER_H

#include <Arduino.h>

class Potentiometer {

private: 
  byte _pin;
  static const int _threshold = 5;        // pot threshold to reduce jittering, default 5 
  static const float _alpha = 0.75;       // smothing filter coefficient, default 0.75 
  static const int _sampleRate = 20;
  static int _counter;

  int _lastValue = 0;        // last potentiometer value
  float _leakyAverage;       // leaky average variable   
  int _rawOutput;            // output pre-smoothing filter
  int _smoothOutput;         // output post-smoothing filter
  unsigned long _prevMillis;

public: 

  Potentiometer(){}
  Potentiometer(byte pin){
    _pin = pin;
    _counter++;
  }

int readValue();                      // method to read potentiometer value

static int getCounter();

// static void printPotValues();

};
#endif

and this is the CPP file:

#include "Potentiometer.h"

int Potentiometer::readValue(){
  int val = analogRead(_pin);     
  if (_threshold <= abs(val - _lastValue)) {   
    _lastValue = val;
  }
  float lastValueFloat = _lastValue;
  unsigned long now = millis();
    if (now - _prevMillis >= _sampleRate){
      _prevMillis += _sampleRate;
      _leakyAverage = _alpha * _leakyAverage + (1 - _alpha) * lastValueFloat;
    }
  _rawOutput = (int)lastValueFloat >> 3;
  _smoothOutput = (int)_leakyAverage >> 3;
  return _smoothOutput;
}
int Potentiometer::_counter = 0;

int Potentiometer::getCounter(){
  return _counter;
}

// void Potentiometer::printPotValues() {

// }

The concept is correct, but if I were doing this, I'd use a Factory Pattern with a static list of the objects. Same result as what you said, but sort of the opposite way to achieve it.

That would be the right way to go...

Thanks guys. I'll look into it and see what I can achieve.

From top of memory with the Factory Pattern you use a specific class offering a function which you use for instantiating objects instead of using a direct constructor call of the class you want ➜ it's a run time thingy then, not something you can declare as a global variable.


You could do some fun stuff with variadic, templates and auto that would let you achieve a simple main sketch like this

auto allPotentiometers = makePotArray(A0, A1, A2, A3, A4, A5);

void setup() {
  Serial.begin(115200);
  allPotentiometers.printValues();
}

void loop() {}

makePotArray would instantiate your Potentiometer class using the variable list of parameters.

click to see the full code
class Potentiometer {
  public:
    Potentiometer(uint8_t value) : value(value) {}

    void printValue() {
      Serial.print(value);
    }

  private:
    uint8_t value;
};

template <typename... Args>
class PotArray {
  public:
    PotArray(Args... args) {
      size = sizeof...(args);
      potentiometers = (Potentiometer*)malloc(size * sizeof(Potentiometer));
      fillArray(0, args...);
    }

    void printValues() {
      for (int i = 0; i < size; ++i) {
        potentiometers[i].printValue();
        Serial.write(i == size - 1 ? '\n' : ' ');
      }
    }

    ~PotArray() {
      free(potentiometers);
    }

  private:
    template <typename T>
    void fillArray(int index, T value) {
      potentiometers[index] = Potentiometer(value);
    }

    template <typename T, typename... Rest>
    void fillArray(int index, T value, Rest... rest) {
      potentiometers[index] = Potentiometer(value);
      fillArray(index + 1, rest...);
    }

    Potentiometer* potentiometers;
    int size;
};

template <typename... Args>
PotArray<Args...> makePotArray(Args... args) {
  return PotArray<Args...>(args...);
}

auto allPotentiometers = makePotArray(A0, A1, A2, A3, A4, A5);

void setup() {
  Serial.begin(9600);
  allPotentiometers.printValues();
}

void loop() {}

Thanks a lot for this. I can see that I have a lot of reading to do to fully understand your code and all the other suggestions... but I am grateful for your directions ! Having a code example to study help a lot! Cheers!!

I'm not sure it's worth all the trouble - it's more a C++ exercise...

You already have an array that is easy to use and holds all the instances

if you don't want the clutter in your loop(), just write helper functions doing what you want by iterating on the array

void updateAllPots() {
  for(int i = 0 ; i < POT_NUM; i ++) {
      potReading [i] = Pot[i].readValue();
#ifdef DEBUG
      Serial.print(F("Pot #")); Serial.print(i + 1); 
      Serial.write('\t');                                          
      Serial.print(Pot[i].readValue());
      Serial.write((i == POT_NUM - 1) ? '\n' : '\t');                                          
#endif
  }
}

then in your loop you just have one line

void loop() {
  updateAllPots() ;
}

keep it simple :slight_smile:

I think you are right! at the moment I am trying to improve my knowledge and coding abilities so I did take it as a C++ exercise, absolutely. But seeing that is way above my level it's best to keep it simple for now. I will come back to this when I am more familiar with some aspects of C++ that I still need to grasp.... It was still worth making the enquiry, sometimes things are more simple than expected (not on this occasion though :stuck_out_tongue_winking_eye: ).
:slight_smile: Thanks a lot!!

the other solutions pointed by @Delta_G and @cedarlakeinstruments are also worth exploring if you want to learn about C++

Will do !

thanks for correcting this... much nicer!!