OOP : creating instances of one class, and control them using another class

Hi everyone,

I'm facing some complex (for me) OOP problem. I am building some kind of automate to be used for lab experiments. Among other tasks, this automate has to control two syringe pumps. The pumps are basically steppers motors. I use two of them to be able to have a continuous flow, when one syringe is empty, the other pump starts, i refill the first syringe, when the seconde one is empty it switches back on the first pump...

I had some functional but badly written piece of code to control it, but as i am adding more and more functions to the automate (pumps, flowmeters, valves, sensors...) i decided to rewrite the code OOP style so it is easier to maintain and more modular where each part is independent.

I created a class syringePump that steps motor when asked and keeps track of steps executed to know if the syringe is full or empty.
I instantiate 2 of them because I have 2 pumps

SyringePump pumpA(4,5,6,7); // The numbers are pins fo the stepper driver
SyringePump pumpB(8,9,10,11);

Among other functions it accepts step(), zeroPosition(), actualPosition()...

As I don't want to bother in my main code to control which pump to use... I created another class SyringePumpController that is supposed to do the hard work. I feed it the value of the flow I need and it should use the right pump according to syringe filling with the right steps speed and switch one one pump to another when needed...

I do not want to instantiate SyringePump in SyringePumpController because I want to be able to access to SyringePump from my main loop() too. I would like to do something like :

SyringePump pumpA(4,5,6,7);
SyringePump pumpB(8,9,10,11);
SyringePumpController pumpController(pumpA, pumpB);

declaring my two pumps and then feeding the controller with those objects

After some research, I still don't know how to correctly write the SyringePumpController so it accepts SyringePump instances, a. I've read about pointers... It's really obscure for me.

I wrote a little script to experiment with. It instantiates 2 leds, and a controller that is supposed to switch between two LEDs when called. It is useless, but if I can get this to work, I can get my real script to work too.

What should I write instead of the XXXXXXX to get this script to compile and work properly ?

class LED
{
  int pin;
  public:
  LED(int pinr)
  {
    pin = pinr;
  }
  void init(){
    pinMode(pin, OUTPUT);
  }
  void on(){
    digitalWrite(pin,HIGH);
  }
  void off(){
    digitalWrite(pin,LOW);
  }
};

class LEDController{

  int state;
  XXXXXXXXXXXXXXXXX1
  XXXXXXXXXXXXXXXXX2
  
  public:
  LEDController(XXXXXXXXXXXXXXXXX1,XXXXXXXXXXXXXXXXX2){
    state=0;
  }

  void switchLED(){
    if(state==0){
      XXXXXXXXXXXXXXXXX1.on();
      XXXXXXXXXXXXXXXXX2.off();
    }
    else if(state==1){
      XXXXXXXXXXXXXXXXX1.off();
      XXXXXXXXXXXXXXXXX2.on();
    }
  }
  
};

LED led1(13);
LED led2(12);
LEDController(XXXXXXXXXXXXXXXXX1,XXXXXXXXXXXXXXXXX2);


void setup() {
  // put your setup code here, to run once:
  led1.init();
  led2.init();
}

void loop() {
  LEDController.switchLED();
}

Thanks for your useful help !

Julien

You can instantiate the pump LED inside the controller and still have access to it from main:

class LED
{
public:
  LED( pin );
  ...
};

class LEDController
{
  public:
    LEDController( pin1, pin2 )
      : state( 0 ), LED1( pin1 ), LED2( pin2 )
      {}

    uint8_t state;
    LED LED1, LED2;

    void switch()
    {
      if (state == 0) {
        state = 1;
        LED1.off();
        LED2.on();
      } else {
        state = 0;
        LED2.off();
        LED1.on();
      }
    };
};

LEDController controller( 12, 13 );

void setup()
{
  controller.LED1.init();
}

void loop()
{
  controller.switch();
}

However, accessing member data directly (without the controller’s knowledge) could cause trouble if the controller is really supposed to be THE only “controller”. To enforce that, you can “hide” LED1 and LED2 in a private section. Actually, state should probably be hidden, too. Then provide extra methods, perhaps like this:

class LED
{
public:
  LED( pin );

  void init() { off(); ... };

  void on() { ... };
  void off() { ... };
};

class LEDController
{
  private:
    LED LED1, LED2;
    uint8_t state;

  public:
    LEDController( pin1, pin2 )
      : state( 0 ), LED1( pin1 ), LED2( pin2 )
      {}


    void init() { LED1.init();  LED2.init() }; 
 };

LEDController controller( 12, 13 );


void setup()
{
  controller.init(); //  inits LED1 and LED2
}

void loop()
{
  controller.switch();
}

If you really need see the current value of state, you can add an accessor function, and change the member name:

  private:
    uint8_t _state; // leading underscore

  public:
    uint8_t state() const { return _state; } // a safe, read-only way to get the current value
};

void loop()
{
  if ( controller.state() == 0) {  // <--  note "function call" parentheses
    ...something
  }

Bonus: the compiler optimizes the wazoo out of short accessors like this so that accessing _state is not really a function call. google “C++ accessor mutator”.

Cheers,
/dev

Why the hell is the class for the pump called LED? That is a STUPID name for a pump class.

/dev thanks for your answer, I will give it a try it once back home

PaulS:
Why the hell is the class for the pump called LED? That is a STUPID name for a pump class.

I think you didn’t read, and i think that your comment is both useless and “STUPID” but I’ll answer it anyway…
If you had read, you would know that this is a dummy script with two LEDs for simple debugging, this is not my real script… as told before, my classes are called syringePump and syringePumpController in the real script.
I prefer to require help for a minimalistic script instead of posting my script with multiple other classes. This way someone with the same question can find the answer quickly without having to crawl thousands lines of code to get the point.