library: how to code for multiple instances?

How can I make use of multiple instances in one function inside a library?

Say I have a library similar to the one shown here and 2 LEDs connected to pin 11 and 12, but both LEDs have their GND-pin connected to pin 1 so that I can switch them also with pin 1. I know this is a silly way to connect LEDs, but this way I can explain what I mean, so please consider it just as an illustrating example. My begin() function would look like this:

void Morse::begin(int pin) {
  pinMode(pin, OUTPUT);
  digitalWrite(pin, HIGH);

  pinMode(1, OUTPUT);
  digitalWrite(1, LOW);

  delay(200);
  digitalWrite(pin, LOW);
}

Now I create 2 instances:

Morse morse1(11); // LED at pin 11
Morse morse2(12); // LED at pin 12

To begin() them I need to do this in my main sketch:

morse1.begin();
morse2.begin();

The result will be:

  pinMode(11, OUTPUT);
  digitalWrite(11, HIGH);
  pinMode(1, OUTPUT);
  digitalWrite(1, LOW);
  delay(200);
  digitalWrite(11, LOW);

  pinMode(12, OUTPUT);
  digitalWrite(12, HIGH);
  pinMode(1, OUTPUT);
  digitalWrite(1, LOW);
  delay(200);
  digitalWrite(12, LOW);

But I want it nested like this:

  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  digitalWrite(11, HIGH);
  digitalWrite(12, HIGH);

  pinMode(1, OUTPUT);
  digitalWrite(1, LOW);

  delay(200);

  digitalWrite(11, LOW);
  digitalWrite(12, LOW);

So I need a begin() function in the library that does something like:

void Morse::begin() {
  pinMode(pins_of_all_instances, OUTPUT);
  digitalWrite(pins_of_all_instances, HIGH);

  pinMode(1, OUTPUT);
  digitalWrite(1, LOW);

  delay(200);

  digitalWrite(pins_of_all_instances, LOW);
}

Is that possible?

My begin() function would look like this:

No, it wouldn't. delay() has no place in a begin() method.

Is that possible?

Not as written.

Think about what would happen if a new instance was created after the (static) begin() method was called.

PaulS:
delay() has no place in a begin() method.

Sorry for my poor English, as mentioned the code is just for illustration: so please replace delay() with whatever command you prefer and begin() with any function name.

jpk:
Sorry for my poor English, as mentioned the code is just for illustration: so please replace delay() with whatever command you prefer and begin() with any function name.

Then, I don't see why each instance of the class can't initialize it's pins and states and whatever else it needs to, completely independent from any other instance.

If there is some need for the instances to communicate with each other, add methods to the class that take an instance of the class. One instance can call it's communicateWithAnotherInstance() method, with the instance to talk to. The hidden field, this, is how the caller's address is known.

If there is some kind of need for the class to know about all the instances, that is what static methods and static variables (and arrays) are for. When the constructor is called to create an instance, it can pass this to a static method, to register the instance being created. The destructor can pass this to a static method, to unregister the instance being destroyed.

PaulS:
I don't see why each instance of the class can't initialize it's pins and states and whatever else it needs to, completely independent from any other instance.

Please have a look at the code snippets I provided in the first posting and you will understand why.

Please have a look at the code snippets I provided in the first posting and you will understand why.

I did look at your code, and, no I can't understand why.

If one thing should control n pins, you shouldn't have n things.

OK according to your signature :sunglasses: I try to simplify my question: what is the syntax for doing the following by calling morse1.function():

void Morse::function() {
 if (instance "morse2" exists) do stuff;
}

what is the syntax for doing the following by calling morse1.function():

There are only two ways for one instance of a class to know whether another instance exists.

One is to have instance one tell instance two that it exists. This means that each instance of the class has to know that another instance might exist. Or, potentially, that multiple other instances can exist.

Do, you need an instance to the class to be aware of one other instance or more than one other instance?

The other way is for the class to know about all instances. This requires that the class have static methods to register and unregister instances and a static field (array, in this case) to contain the instance addresses.

The problem with this approach is that there is no way to define which two instances ought to cooperate. One could use two arrays, and static methods, to allow an instance (that the class already knows about) to say which other instance(s) it wants to cooperate with.

However, I can not imagine why one instance of a class to send data via Morse code ought to cooperate with another instance of the class. THAT is the bit you haven't explained.

jpk:
So I need a begin() function in the library that does something like:

void Morse::begin() {

pinMode(pins_of_all_instances, OUTPUT);
  digitalWrite(pins_of_all_instances, HIGH);

pinMode(1, OUTPUT);
  digitalWrite(1, LOW);

delay(200);

digitalWrite(pins_of_all_instances, LOW);
}



Is that possible?

yes, using a static array of instances you can accomplixh that...

Something like this:

#define MAX_INSTANCES 10

class Example{
  public:
    Example(int _pin); 
    static size_t instanceCount;
    static void begin();
  private:
    int pin;
};

size_t Example::instanceCount = 0;
Example* instances[MAX_INSTANCES] = {nullptr};

Example::Example(int _pin)
{
  pin = _pin;
  instances[instanceCount++] = this;
}

void Example::begin()
{
  for (auto i : instances)
  {
    if (i) 
      {
        pinMode(i->pin, INPUT_PULLUP);
      }
    else
      {
        break;
      }
  }
  pinMode(1, OUTPUT);
  digitalWrite(1, LOW);
  delay(200);
  for (auto i : instances)
  {
    if(i)
      {
        digitalWrite(i->pin, LOW);
      }
    else
      {
        break;
      }
  }
}

Example foo(4);
Example bar(3);

void setup() 
{
  Example::begin(); 
}

void loop() 
{
  
}

Many thanks, this is exactly what I was looking for!

But I cant' figure out how to prevent Example::begin() from doing digitalWrite(i->pin_B, LOW) multiple times in case Example foo() and Example bar() share one of their pins:

Example foo(4, 12, 5);
Example bar(3, 12, 6);

How can I make sure not to call things multiple times? I would like to switch a boolean and do something like this

bool pin_B_set_low = 0;
  if (pin_B_set_low == 0) {
    digitalWrite(i->pin_B, LOW);
    pin_B_set_low = 1;
}

But I don't know how to detect if foo() and bar() use the same pin as their pin_B (in the above example pin 12). Please help!

??

Just don't form constructors with the same initializing values...

PaulS:
delay() has no place in a begin() method.

There are legitimate reasons to use delay() in a begin() method.
For example, there may be some hardware timing or device initialization that is required that is quite lengthy.
And in some cases even if the delay requires is sub millisecond, you may still have to call delay(1) to ensure that a watchdog timer does not fire. This is the case on the ESP8266 platform.
While there are many real world use cases, a great example of a having to use delay() is for hd44780 LCD initialization. Part of the sequence that gets the LCD back into 8 bit mode so it can be reliable be put into 4 bit mode requires some blind waits as even if the R/W signal is hooked up, the BUSY status cannot be used during this part of the initialization.
Those blind waits/delays are specified to be 4.1ms and 100us and there is no way to avoid these.
While you could call delayMicroseconds() instead of delay() that can cause watchdog timeouts on certain platforms.

Now if you really meant don't use delay() in a constructor instead of a begin() method then I would agree with you.

--- bill