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.

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.

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?

@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:)

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?

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/

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++.

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.

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?

Your sketches of post #12 are tested and they work fine.

The main problem with the vast majority of C++ learning resources is that they assume you are using a desktop Linux box with unlimited memory and a hardware memory management unit.

A microcontroller is a very different environment because you have a severely limited memory size plus you have to be mindful of the fact that repeatedly creating and deleting objects on the heap may cause your program to fail due to memory fragmentation issues. In addition every time you use a C++ library such as the STL you have to ask the question "is this going to cause problems", because it is unlikely that the author of the library ever thought about it being used on a microcontroller.

You typically work around this problem by creating a bunch of objects at startup instead of dynamically creating and destroying objects on an as needs basis as you would do with a typical desktop or server application.

Hello

appeal to authority

//appProg-2.ino

#include "Service.h"
#define LED2 2
#define LED3 3
#define LED4 4

Blinker blinkers[] =
{
  Blinker(LED2, 200, 800),
  Blinker(LED3, 500, 500),
  Blinker(LED4, 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();
  }
}

//Service.h

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();
};

//Service.cpp

#include "Arduino.h"
#include "Service.h"

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));
  }
}

appProg-2.ino (366 Bytes)
Service[1].h (344 Bytes)
Service[1].cpp (350 Bytes)