Arduino the Object Oriented way

I response to many and various threads on this programming boards, I have done a bit of a write-up on how to go about doing Arduino projects by treating the components of your sketch as objects.

The write-up is here on github.

ArduinoTheOOWay.html

Don't know if it's any use to anyone, but I'd like to hope so.

1 Like

Hi,

Thanks mate, have just copied it , will do some holiday reading and experimenting.
Looks good on just a quick scan.

Tom… :slight_smile:

Couple of nit comments:

Why int here and not byte?

const int BUTTON_PIN = 7;

Same with state, prevState, pin. variables that are only 0/1 don't need to be ints, and there are no pins above 255, so they also don't need to be int. You have int's all thru the example that could be byte instead.

I like the explanation you provide. This looks like stuff I just code inline in my sketches. MrsCrsossRoads ridicules me for it, but I figure I'm saving processing time by not jumping back & forth in & out of functions, just performing the simple digitalWrite of whatever is starting the class. Also I don't want to take the time to create all that stuff every time, nor can I remember how every class created over the years is used. Basically sounds like I'm a lazy, not too bright coder! I think having to figure that stuff out every so often helps me to keep thinking about what's really going on tho. My fencing scoring machine is 25 pages long of code - no functions except setup() and loop(). No classes, etc. The code acts like it tho with a couple of flags to indicate between sections that something needs performing - when a score is updated for example, the score variable is updated, a displayUpdate flag is set - the updateScore test gets its shot and checks the flag and updates the display as needed, clearing the flag. All the code is inline, and if you read it it's basically what someone might write as 3 functions: void loop(){ checkRFinput(); checkTouchInput(); updateTimeScoreDisplay(); } However, I also don't like jumping back and forth in a sketch trying to find a section that is referenced, which also leads to my preference for inline code. Chalk it up to poor programming preferences by a hardware designer looking for speed over code niceness.

CrossRoads: Couple of nit comments:

Why int here and not byte?

const int BUTTON_PIN = 7;

Same with state, prevState, pin. variables that are only 0/1 don't need to be ints, and there are no pins above 255, so they also don't need to be int. You have int's all thru the example that could be byte instead.

I use an int because the pinMode and digitalWrite functions take ints.

In justification: the arduino is a 16-bit processor, so it's all just one bus read anyway. Using ints means that things wind up on even byte boundaries, which might be a good thing (It was a thing back in the day on x86 processors). If your sketch is just one byte short of being able to fit into ram, you probably need a mega.

If you have big arrays then that's a different story, agreed.

CrossRoads: I like the explanation you provide. This looks like stuff I just code inline in my sketches.

Absolutely. Classes just provide a way of organising your code. The difference between using classes and not using them is like the diference between using block-structured code and using gotos. Block-structured languages happened because people started noticing that coders followed consistent idioms when writing loops, ifs, and all the rest. Classes are the same - they are formal support in the language for things that people are doing anyway.

CrossRoads: MrsCrsossRoads ridicules me for it, but I figure I'm saving processing time by not jumping back & forth in & out of functions,

Yeah, there are inefficiencies doing it this way because class methods can't assume an absolute location for your variables. In fact, the memory address of the instance is passed invisibly to every method. If you need hand-tuned speed, working this way won't do the job. But, it's not that bad. At a guess, the instance address goes into a register, and the generated code uses register-relative addresses.

If microseconds matter then this pattern is not suitable, and for some microcontroller projects, microseconds do matter. The really high-speed arduino stuff with be working with interrupts and ports. Even then, however, it can make sense to use classes to do things that operate at human speed - manual switches, blinky lights.

The main benefit is that you can code up complex things that mostly work first time, without burning out your brain keeping track of all the possible permutations of state in your system.

"I use an int because the pinMode and digitalWrite functions take ints." " Using ints means that things wind up on even byte boundaries, which might be a good thing "

In the datasheet 8.3 SRAM Data Memory shows the SRAM Memory as 2048 x 8 in Figure 8-3. Data Memory Map, which would seem to be in disagreement with the latter statement.

And this also says to me that ints are not required to use SRAM bytes directly: "The direct addressing reaches the entire data space. The Indirect with Displacement mode reaches 63 address locations from the base address given by the Y- or Zregister. When using register indirect addressing modes with automatic pre-decrement and post-increment, the address registers X, Y, and Z are decremented or incremented."

CrossRoads: In the datasheet 8.3 SRAM Data Memory shows the SRAM Memory as 2048 x 8 in Figure 8-3. Data Memory Map, which would seem to be in disagreement with the latter statement.

Ok. Use bytes, then.

PaulMurrayCbr: I use an int because the pinMode and digitalWrite functions take ints.

In justification: the arduino is a 16-bit processor, so it's all just one bus read anyway. Using ints means that things wind up on even byte boundaries, which might be a good thing (It was a thing back in the day on x86 processors). If your sketch is just one byte short of being able to fit into ram, you probably need a mega.

All of which is neither here nor there. If the value will fit into a byte, there is no benefit whatsoever to putting it into an int. The compiler will cast it to an int when passed to a function that takes an int. And worrying about byte alignment is equally pointless. The compiler will automatically enforce any memory alignment requirements imposed by the processor architecture. You don't need to "help" it.

Bottom line, you're wasting RAM, without getting anything whatsoever in return.

Regards, Ray L.

I cant help feeling that people responding on this thread are missing the point a little.

PaulMurrayCbr: I cant help feeling that people responding on this thread are missing the point a little.

I feel ya buddy :) It's a good write up.

I have bookmarked your link - thank you for taking the time to write a clear and simple tutotial.

That said, I always have the feeling that OOP is bolted onto C as an afterthought. It is constantly in my mind "I don't actually need all this s**t"

Whereas, in Ruby EVERYTHING is an object and you can't NOT use objects. However that does not require you to create classes just to do simple things with Ruby. If you create variables that "appear" to be outside any class they are actually part of a class (forget what it is called). This makes it very easy (in the Arduino sense of easy) to get started with Ruby.

I gave up Ruby in favour of Python because Python is more widespread (at least in the Arduino community) and because Python is better integrated with its environment (e.g. there is only one pySerial that works on all platforms). But OOP in Python has the same "bolted-on afterwards" feel as it has in C/C++.

The best example I know of how OOP can kill the will to live is Java - even though I think the JVM is excellent. I wonder how many people have given up programming completely because their first exposure was to Java.

...R

Yes Robin, we have all heard many times how you prefer doing things with out classes etc... (sometimes the hard way, just because code is inside a class and may not expose a newbie to every concept used inside it).

Your very biased attitude towards interfaces and objects is most probably what is holding you back. It seems you gave up on them before learning enough to see the real benefit in using them for what they are.

Being able to encapsulate a few features like shown in these examples is only scraping the surface. The C++ type system is built around having the ability to extend it.

If you need a feature/type that is not provided in the standard, you can make it. By using operators correctly you can create new types which leave your top level code (sketch) looking like nothing more than a C program. None of which has anything to do with the object orientated paradigm (that's all it is, an idea, a method of using classes a certain way, not a requirement).

The more you learn, the more you'll see their benefit (the learning curve isn't really a reason to hide it from newbies either).

I use an int because the pinMode and digitalWrite functions take ints.

No.

void digitalWrite(uint8_t pin, uint8_t val)
.....

void pinMode(uint8_t pin, uint8_t mode)
....

See: wiring_digital.c

pYro_65: Yes Robin, we have all heard many times how you prefer doing things with out classes etc.

I think my comments are as reasonable as yours are, but I have not made any personal criticisms.

...R

Robin2: I think my comments are as reasonable as yours are, but I have not made any personal criticisms.

...R

You're opinions are just as valid as the next persons.

I'm not criticising you personally, I'm criticising your opinions you openly voice regularly on the forum regarding classes/objects. Refuting an opinion is not an attack on the person voicing the opinion.

I'm sorry if you feel like I'm attacking you, It just seems like you constantly argue your opinion against a topic which is well and truly proven beneficial by millions of programmers. So I feel it appropriate to argue against it... Fair?

And many times I've seen posts with people offering new ideas or concepts using classes only to have their work criticised because what it offers can be done without a class. And sometimes it has ended up with you mentioning that you do not fully understand the concepts being used. Which is fine, but still a biased influence.

combie: No.

void digitalWrite(uint8_t pin, uint8_t val)
.....

void pinMode(uint8_t pin, uint8_t mode) ....



See: wiring_digital.c

Well, that's different then. I have modified the page.

Robin2: That said, I always have the feeling that OOP is bolted onto C as an afterthought.

You are absolutely right - it was. C++ was originally a preprocessor that generated C, back in the day. I learned C back in '85 from the K&R blue book - no fancy objects and constructors back then. Likewise, closures and streams are bolt-ons to Java. Things change.

C++ is certainly not 'C with extras' any more. Modern C++ features such as ranged loops, lambda functions, auto type deduction and so on really make C++ a high-level language without really sacrificing much, if anything, in terms of performance.

pYro_65: You're opinions are just as valid as the next persons.

I'm not criticising you personally,

When you use the words "Yes Robin, we have all heard many times" and "Your very biased attitude" it is hard to escape the feeling that the comments are personal, and will be seen as such by others.

It would not have been difficult to say "I disagree with XX and I prefer to do things YYY"

I am quite happy to accept that there is no personal animosity.

...R

In justification: the arduino is a 16-bit processor

No, it's an 8 bit processor (except for the ones that are 32bit processors.)

Modern C++ features such as ranged loops, lambda functions, auto type deduction and so on really make C++ a high-level language without really sacrificing much, if anything, in terms of performance.

I'd like to see an Arduino example using one or more of those. An awful lot of the time, for C++ programmers, "no performance sacrifice" means "if your actual data was 1MB, encapsulating it all in a useful and elegant C++ container only adds another 100k, and obviously 10% is not very significant" rather than "the object overhead fits in 2k with plenty of room for actual data."

PaulMurrayCbr: Don't know if it's any use to anyone, but I'd like to hope so.

As an OOP tutorial held up to novices, the article is highly opinionated, dubious even, in places.

Especially.

  • Neglecting to mention the cost of OOP.
  • Overuse of private properties
  • brightnessClicker 'should' [not] be a property of headlamp - Breaks the MVC pattern.
  • Calling millis() repeatedly
  • Blocking a refresh method for 800,000 clock cycles.

"The problem is that there are then two places [where a click is started]" The redundancy can be reduced logically, by sticking to a read/modify/write pattern.

I see no tangible benefit in the Runnable class. As such, the class adds unnecessary complexity, consuming RAM and clock cycles needlessly.

Personally I would not call simple inheritance polymorphism, even with virtual functions - It's simply, inheritance. Multiple inheritance and overriding methods, would be examples of polymorphism, to my mind.

"It's called 'time-slicing'" No, not really. Time-slicing is an established term, which implies pre-empting, by a scheduler.

As a set of notes documeting your thought process, I think the article is great.

"Writing this stuff is much quicker and easier than writing about it." On which, we are in full agreement.