Correct usage of C++ classes and OOP book advice for Arduino

Hello there. Recently I have started to apply user-defined classes (classes that I implement myself) when working with different programming languages and the same I decided to do with Arduino (I am a novice with four months of serious engagement). One of my projects was to make a digital clock with using C++ and by this moment the code has been quite long (about 860 lines of code) and I use a keyword such as "class" to separate components connected to Arduino. My question is if OOP is quite acceptable with Arduino? Should I use it at all? If so, it's not a huge problem for me to use classes, but I am not sure I implement them correctly (I didn't work with classes much). I mean I am not sure I connect different objects in the correct manner. Is there a good book to make it possible to improve it anyhow? May all that be improved in any other way as well? Also I can demonstrate my code here if it's needed. And I do appreciate your paid attention here.

Personally, I have found it very convenient to use classes in my code as it organises myself as a programmer. And I think all pros and cons told about using them are actually true. The only question is how to do that correctly.

1 Like

I see c++ used in the libraries but not in programs in the arduino world. I think learning more about c++ would help me understand the libraries much better as far as inheritance goes. Some of the syntax I just don't understand.

1 Like

It's the only language we use. Sometimes you see C in libraries. But everything in a .ino file is C++.

OOP is definitely a must have for any sort of large programs. It just makes things so much easier to build in pieces. There are lots of resources on the internet to learn to build classes in C++. There are resources on this site.

http://paulmurraycbr.github.io/ArduinoTheOOWay.html

The Arduino hardware (memory sizes) can limit the use of C++ and OOP. Just the 8 bit Arduinos don't have reliable dynamic memory management due to small RAM size, and not all C++ standard libraries are fully ported to these controllers.

So you should start with a reasonably large Arduino or you risk to crash your programs by the use of the C++ String type or other dynamic memory items.

Also have a look at some Arduino core classes, how these are instantiated and initialized, and which C++ functionality they use.

If you are new to C++ then I'd suggest a less critical environment for your first steps, i.e. a PC. There you'll also have better debug support, not limited by flash write cycles and other MCU restrictions.

In terms of numerical figure, how many lines of codes could be considered large?

That's kind of a ridiculous question. How many pounds is considered heavy? How many apples is a lot of apples? What's the distance before it's considered far?

And in this sense it really isn't about number of lines. It's about how much stuff you got crammed into them. When OOP really comes in is when you've got a sensor, and a wifi connection, and an SD card, and some lights, and something else, and something else. OOP helps to keep all of those things your code needs to do organized.

@GolamMostafa, there is a rule of thumb from the old days that a function should fit on a page of A4. Printed on an old fadhioned marix printer.

Nowadays I guess it's more or less equivalent to "it should fit on a screen" (and yes, I know there are varying screen sizes :wink:)

1 Like

Mostly covered already - Arduino is programmed in C++, so classes if you want them. Dynamic memory allocation is risky, especially on smaller Arduinos. Note that it's easy to use allocation without realizing it e.g. copy constructor.

No need to go all-in on classes though - just make a few small ones that rationalize your code, even if they're not adding much.

As to learning - any C++ book should cover classes. It took reading several texts before OOP clicked though.

When we say -- it is hot, then the degree of hotness is to be specified for the satisfactory design of the Fuzzy Controller.

It is NOT I who has raised this question (the meaning of large) for the first time. He is
Bjarne Stroustrup (inventor of C++) who observed that a program became unmanageable and unprotected when it grew up larger and larger and then he conceived the idea of class keyword to break the large prgram (someone says > 50,000 lines) into modular/protected forms with the assistance of these access specifiers: private, public, and protected.

Now-a-days, we observe that a Library File is full of C++ Codes though the total lines are very much less than 50, 000 lines. Moreover, the sketch that uses that Library works in a single user embedded system having no protection issue. I am still trying to understand the advantages of putting class based codes in those Libraries.

If someone says that class based codes offer modularity, then I would say that modularity could also be given by designing functions/suroutines. The key query is: what was the fundamental motive behind the dvelopment of C++? Was it to offering modularity to a large program or of offering protection to varaious components of a large program in a multi-user environment?

For example:
The built-in LED of UNo Board can be blinked at 1-sec interval by the following non-class sketch:

#define LED 13

void setup()
{
  pinMode(LED, OUTPUT);
}

void loop()
{
  digitalWrite(LED, HIGH);
  delay(1000);
  digitalWrite(LED, LOW);
  delay(1000);
}

The above blinking task can also be accomplished by the following class based sketch which I see as an academic exercise instead of having any advantage over the non-class sketch.

class DigitalIo  //Class Name (DigitalIo); do Capitalize the first letter for Class Name
{
  private:
    int ledPin;  //variable can only be accessed by the functions under public: specifier

  public:
    DigitalIo(int DPin);//: ledPin(powerpin) {} //inline constructor to initilize ledPin
    void ioDir();  //member function
    void ledOn();
    void ledOff();
};

DigitalIo led(13);  //led is called object of type DigitalIO

void setup()
{
  Serial.begin(9600);
  led.ioDir();     //setting up the direction of IO line(13) as output
}

void loop()
{
  led.ledOn();
  delay(1000);
  led.ledOff();
  delay(1000);
}

void DigitalIo::ioDir() //member function definition; :: (double colon) is calld scope resolution operator
{
  pinMode(ledPin, OUTPUT);
}

void DigitalIo::ledOn()
{
  digitalWrite(ledPin, HIGH);
}

void DigitalIo::ledOff()
{
  digitalWrite(ledPin, LOW);
}

DigitalIo::DigitalIo(int x)
{
  ledPin = x;
}

Yes, it is acceptable. As people have pointed out, it's very common in Arduino libraries.

OTOH, one of my pet peeves is "making everything an object", even when it is awkward and perhaps counter-intuitive to do so. Microcontroller and "Physical computing" tend (IMO) to be very event and procedurally oriented, and that doesn't always map well to objects. I'm not sure how OOP works for your "digital clock" example, for instance.

Say, you are asked to add two 8-bit numbers (for example: 0x23 and 0x45) using Arduino UNO and a calss based sketch. In this example, you have two variables; one function to intialize the variables; another function to add the variables. Can you show your codes where the class keyword will integrate these components and then will work with setup() and loop() functions to produce the result?

You are correct that in that case it is a trivial exercise and doesn't help much. You're blinking a single led. OOP doens't really help much when you only have one of the object.

Now rewrite that same sketch to blink all the pins at different rates. I think in that case you can see how a class can greatly reduce code duplication.

Here's an example with three pins:

First the normal way, with arrays so it's still short:


uint8_t pin[3] = {2,3,4};
uint32_t onPeriod[3] = {200, 500, 800};
uint32_t offPeriod[3] = {800, 500, 200};
uint32_t lastChange[3];
bool state[3] = {false, false, false};

void setup() {
  for (int i = 0;i<3;i++){
    pinMode(pin[i], OUTPUT);
  }
}

void loop() {
  for (int i = 0;i<3;i++){
    uint32_t currentTime = millis();
    if(currentTime - lastChange[i] >= (state[i] ? onPeriod[i] : offPeriod[i])){
      state[i] = !state[i];
      lastChange[i] = currentTime;
      digitalWrite(pin[i], (state[i]?HIGH:LOW));
    }
  }
}

The code isn't long. But all those arrays are cumbersome. The pin numbers and the on and off times are all in separate arrays. If I want to add more then I have to go through and add a piece to each array. And if I was doing something more involved than blinking a light, I might have a lot more than three or four variables to keep track of for each instance.

Now when I make it a class:


class Blinker {
private:
  uint8_t _pin;
  uint32_t _onPeriod;
  uint32_t _offPeriod;
  uint32_t _lastChange;
  bool _state;

public:
  Blinker(uint8_t pin, uint32_t onPeriod, uint32_t offPeriod)
    : _pin(pin), _onPeriod(onPeriod), _offPeriod(offPeriod), _state(false) {}
  void begin();
  void run();
};

void Blinker::begin() {
  pinMode(_pin, OUTPUT);
}

void Blinker::run() {
  uint32_t currentTime = millis();
  if (currentTime - _lastChange >= (_state ? _onPeriod : _offPeriod)) {
    _state = !_state;
    _lastChange = currentTime;
    digitalWrite(_pin, (_state ? HIGH : LOW));
  }
}


Blinker blinkers[] {
  Blinker(2, 200, 800),
  Blinker(3, 500, 500),
  Blinker(4, 800, 200),
};

void setup() {
  for (int i = 0; i < 3; i++) {
    blinkers[i].begin();
  }
}

void loop() {
  for (int i = 0; i < 3; i++) {
    blinkers[i].run();
  }
}

Number of lines of code actually went up considerably. That's due mostly to the trivial nature of what this code does. In something more involved the code might be a lot simpler.

However, I have gained some notable advantages now. Each of my Blinker objects is defined in one place with all the variables together in the blinkers array. I can easily see everything for one instance together at once. And if I want to add another instance, I only have to add one entry to this one array.

My loop code gets much simpler, and this is key too. Because if this is something that I do a lot, then I can put this class into a library file and use it repeatedly in many sketches. That means I wrote this once and from now on blinking a led is three lines of code, instantiate, begin() and run(). For something more involved than an led, you can certainly see where this would be a great advantage.

The array example could be made into a library as well. But it wouldn't be as easy or as neat or as simple to work with. I'll leave that as an exercise for you if you want. I think that will convince you of the utility of a class.

1 Like

Hello fixer_84

Yes, all in all, a "class definition" for an OOP sketch for an Arduino is not necessary. C++ is not only defined by class definitions.

Here comes a simple BWOD example with structured arrays.

//https://forum.arduino.cc/t/correct-usage-of-c-classes-and-oop-book-advice-for-arduino/1168947/1
//https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
#define ProjectName "OOP BWOD without class definition"
#define NotesOnRelease "first proposal"
// make variables
//--------------------------------------------
// add port pin addresses and flash times and compile again
constexpr uint8_t OutPut[] {9};
constexpr uint32_t FlashTime[] {1000};
//--------------------------------------------
// make structures
struct BWOD
{
  uint8_t pin;
  uint32_t previousMillis;
  uint32_t intervalMillis;
  void make (uint8_t pin_, uint32_t intervalMillis_)
  {
    pin = pin_;
    pinMode(pin, OUTPUT);
    intervalMillis = intervalMillis_;
  }
  void execute(uint32_t currentMillis)
  {
    if (currentMillis - previousMillis >= intervalMillis)
    {
      previousMillis = currentMillis;
      digitalWrite(pin, digitalRead(pin) ? LOW : HIGH);
    }
  }
} bwods[sizeof(OutPut)];
// make support
void heartBeat(int LedPin, uint32_t currentMillis)
{
  static bool setUp = false;
  if (!setUp) pinMode (LedPin, OUTPUT), setUp = !setUp;
  digitalWrite(LedPin, (currentMillis / 500) % 2);
}
// make application
void setup()
{
  Serial.begin(115200);
  Serial.print("Source: "), Serial.println(__FILE__);
  Serial.print(ProjectName), Serial.print(" - "), Serial.println(NotesOnRelease);
  int element = 0;
  for (auto &bwod : bwods)
  {
    bwod.make(OutPut[element], FlashTime[element]);
    element++;
  }
  Serial.println(" =-> and go\n");
}
void loop()
{
  uint32_t currentMillis = millis();
  heartBeat(LED_BUILTIN, currentMillis);
  for (auto &bwod : bwods) bwod.execute(currentMillis);
}

Have a nice day and enjoy coding in C++.

p.s.

Here comes my recommendation for the assistance with programming in C++.

https://www.learncpp.com/

This code uses a struct.

A struct is like a class. The main difference is that the members of a struct are public by default and are only private if you mark them as such. A class is the opposite, the members are private by default and only public if you mark them.

In the old days I don't think structs could have functions. But now they can. And encapsulating the data but not the methods seems wasteful.

To use a struct and claim you are avoiding OOP is a bit misleading. The struct is an object is it not?

1 Like

Very much!

The side-by-side (non-class and class) examples of post #12 followed by elegant description worth reading/practicing.

Underway - will be posted once completed.

Yes it is.

But not defined via the "class" directive.
That was all I wanted to say with my example for the TO.

Have a nice day and enjoy coding in C++.

It's not much of a difference. If you change only the word class in the code in #12 to struct it will compile and work exactly the same.

In general a class would be preferred to a struct because you want the members to be private to keep encapsulation. Let the user use the api, not abuse your class variables. But you can make sections of a struct private if you want. Really as long as you're marking your public and private sections it's essentially two words for the same thing.

1 Like

A class can seem a bit heavy weight if only one instance of it is ever instantiated and there are then two "things" lying around, the class definition and the instantiated object which have to be named differently . Sometimes a name space could be more appropriate giving the same degree of encapsulation but lacking a structured permissions group. A static class also comes close to this. Of course, if you are creating say multiple buttons on a touch screen then a class (as a sort of cookie cutter) could be a valid approach.

1 Like

Service.h.zip (599 Bytes)
appProg-2.ino (364 Bytes)

Then, probably, Bjarne Stroustrup could avoid the use of class keyword and struct would suffice?