Classes with Arduino

why don't you make your class reusable? handover the pin in the constructor. If you do so - another LED is one line in setup, and one line in loop. That's why one uses OOP - to make things reusable.

make a "begin" member function and call it in setup to set the pinMode.

read again the BlinkWithoutDelay example and check how they use the pattern "millis() timer" correctly.

use camelCase for your member functions (--> use .flash instead of .Flash())

something like

//
// blinks several LEDs in different intervals without using the 'delay' function.
// Written in object orientated style.
//
// Public Domain: Please feel free to use but citation appreciated
// JIT 06/01/23
// applied Arduino style by noiasca


class LED {
  protected:
    const uint8_t pin;
    bool state; // whether the LED is on or off
    uint16_t interval = 1000;
    uint32_t previousMillis = 0;

  public:
    LED(uint8_t pin) : pin(pin) {}

    void begin()
    {
      pinMode(pin, OUTPUT);
    }

    void setInterval(uint16_t interval)
    {
      this->interval = interval;
    }

    void blink(unsigned long currentMillis = millis())
    {
      if (currentMillis - previousMillis > interval) {
        previousMillis = currentMillis;
        if ( state ) {
          digitalWrite(pin, LOW);   // turn the LED off by making the voltage LOW
        } else
        {
          digitalWrite(pin, HIGH);  // turn the LED on (HIGH is the voltage level)}
        }
        state = !state;
      }
    }
};

LED ledA(LED_BUILTIN);  // create a LED instance with the built in LED
LED ledB(12);
LED led[] {11, 10};     // create an array of LED with several pins

void setup() {
  ledA.begin();
  ledB.begin();
  ledB.setInterval(500);  // modify the default interval

  for (auto &i : led) i.begin(); // call begin function for all members of the array led[]
  led[0].setInterval(700);
  led[1].setInterval(900);
}

void loop() {
  uint32_t currentMillis = millis();   
  ledA.blink(currentMillis);
  ledB.blink(currentMillis);
  for (auto &i : led) i.blink(currentMillis); // call blink function for all members of the array led[]
}

@dlloyd OK fair enough.

In fact I discovered I could use classes through reading the library code, and realising if "they" could use classes etc. then so could I since it was all going through the same compiler.

For me "Reference" - the name of the document I refered to - did not imply beginner documents.

@noiasca Nice piece of code!

why don't you make your class reusable? handover the pin in the constructor. If you do so - a another LED is one line in setup, and one line in loop. That's why one uses OOP - to make things reusable.

It had crossed my mind, but really my objective was really to produce a short piece of code to make sure my understanding of C++ classes (and especially in the Arduino environment) was correct.

I'm working towards a couple of real(ish) applications, which may well be maintained and modified by other people, and the proliferation of global variables to keep track of the states of controlled devices was kind of irritating me. Having used OOP in the past it seemed the right solution.

@westfw I think the thing which confused me was precisely that the document I cited was called a "Reference" manual ....

A couple (hopefully seen as constructive) comments:

about this variable name

if it's a bool I would make it obvious what you mean, for example

    bool ledIsOn; // true when the LED is on, false when it's off

and begin() should read that state of the pin and use it to initialise the boolean? (or assumption is setting OUPUT means LOW so false which is the default value as 0 is promoted into false ?)

and I wonder why you pass currentMillis to the blink function at all? is that to share a common time if you blink multiple leds in a row ?

@Coding_Badly I agree with @gfvalvo, or at least I think they have a case.

There are quite a few people who claim OOP is easier to learn than procedural or functional programming.

Don't know if they have evidence ....

short:
more or less yes, but not to be meant as sync.

long:
That's just my way. Sometimes I find it handy to be able to handover the millis in the update function from the outside. Mostly because a millis() call takes some time. If I have some simple like blinking, flashing, ... imho it's ok that all calls are done with the same millis() timestamp.
Furthermore it makes not so much difference if I would have declared a currentMillis in the member function. But to do that as parameter gives me a) the local variable b) the possibility to hand over the millis, or c) just call the member function blink() without the parameter and let the function call millis() itself...

OK handy indeed

PS: your other points are valid. I should have renamed all variables. For my defense: My example wasn't meant to be 100% "Tutorial Safe" :wink: .
The Arduino example BwD uses ledState... I would reuse that. But I would not mention that it is the output - it is the state. So true/false is imho better, the inverse ! correct and the handling of HIGH or LOW according to the digitalWrite API.

slightly updated:

are you referring to the Arduino Language Reference page, which is just a summary of Arduino specific functions, some keywords and operators

See appendix A of The C Programming Language (2ed) for a more complete description of a language and pg 234 for the BNF description

Explainer: What is Arduino? provides a better perspective

hum... we use a C++ compiler so the C specification is not what should be used

it is an example of a language reference, not of C++

The struct keyword was there in C Language before the advent of C++ whose one of the principle characteristics was to conceive the idea of class keyword. What advantage (s) the class keyword did offer, which promoted C to the status of C++?

it's more for historical reasons that struct evolved into class. Initially there was also this concept of polyglot libraries where you can provide a single header file that declares the same API to programs written in C or C++ language.

The common pattern I've seen is that you use a struct if it's mostly to group various data together and you use a class when you start attaching functions operating on this data or other OOP concepts like inheritance.

2 Likes

guessing a struct can't inherit from another struct ... ?
guessing a struct doesn't support virtual elements ... ?
does it?

no really, it's the same

struct A { };
struct B : A { };
struct C : B { };

it's just that stuff are public by default

(virtual too)

does struct have/need private/public/protected(?)?

and are these differences more or less for backward compatibility? if so, with what?

did you suggest that a struct can inherit from a class (in a library)?

With one exception class and struct are the same thing in C++. That exception is that the default access specifier for struct is 'public' while for class it's 'private'. THAT'S IT.

can you provide a reference?

[I would recommend you:

i wouldn't be surprised that there is a very good reason for making this distinction, but more willing to believe early versions of an improved C didn't have the class keyword and hence this is needed for backward compatibility with those very early versions

i can also believe, an organizations coding style guidelines would not allow the use of struct with inheritance.