Using Abstract Classes with Arduino


What is an 'abstract class'?
An abstract class is an 'abstraction', or more simply put - it's a way to tell the computer what you want to do without telling it how you're going to do it. The first time I heard that, it made no sense to me.
So, the way I think about it is that an abstract class is like a game cartridge. The game system knows what to do with the game cartridge when you put it the system, even though it has no idea what game you're playing. How does the game play without the underlying code running it knowing anything about the game code? Well, one way to do that is by using abstract classes in the system code.

Why would I use one?
Simply put, you might not ever use one. You can do a lot of really useful things with an Arduino and never use anything but the single file that you get when you start a new sketch. However, when things start getting complicated you might find the need to start organizing your code into classes, and if you're doing that - depending on the problem you might be trying to solve - you might find it useful to use an abstract class.  Often when modeling things in an object oriented language like C++, it's useful to break things down into components and actions. In many cases, using an abstract class will make it easier to keep things organized and make later changes much less painful. Like a lot of programming ideas, these are just tools in your toolkit. There are usually lots of ways to solve any single programming problem, and there are seldom only one 'right' way to do it. Once you use an abstract class, you might find other situations where you wish you would have used them, and ample opportunities to actually implement them. 

Ok, so how do I make one?
Well, lets start by doing the one thing every Arduino enthusiast can't resist - blink the built-in LED.

Here's the 'Blink' example you will find in `File->Examples->01.Basics->Blink`






First, you'll have to create the new abstract class. In the Arduino IDE, you do this by clicking the little 'down' arrow' located on the top right of the pane and choosing 'New Tab'.




The IDE will then prompt you to make a file name. type `abstract_blink.h` and click OK.



Make sure you save your work by clicking File->Save, or CTRL+S on your keyboard!

Congratulations, you now have 2 tabs on your IDE.




Now, we have to 'implement' the abstract class. Before we do that, we have to think about what this class will 'do'. In our case, all this will do is blink the built-in LED, so we'll make a new abstract class in abstract_blink.h called abstract_blink, and it will have one public function that returns void (which just means it doesn't return anything!)  Click on the abstract_blink.h tab to select the file, and add this code:

class abstract_blink{
  public:
  virtual void doBlink(){};
  abstract_blink(){};
  ~abstract_blink(){};
                                
};

It's the 'virtual' keyword there that makes this class abstract. We're telling the computer that we're going to give it an object (that we'll create next) that will have at least one method in it called 'doBlink()', that accepts no arguments and returns nothing. 


Next, we need to create at least one more class that 'implements' our new abstraction. 
So, add a new tab called `my_first_blinker.h`, and another tab called 'my_first_blinker.cpp`. 
(If you're not familiar with header files, and what they're used for, let me know in the comments and I'll write a separate blog about that.)
In `my_first_blinker.h`, define your new class:

#ifndef abstract_h
#define abstract_h
#include "abstract_blink.h"
#endif
class my_first_blinker : public abstract_blink
{
  public:
  void doBlink();
};

Then implement your class in `my_first_blinker.cpp`


#include "my_first_blinker.h"
#include <Arduino.h> // for LED_BUILTIN and HIGH
void my_first_blinker::doBlink() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}


We're almost there, I promise!

We need to include our abstract class and our new implementaion of that class into our main sketch at the top. 

#ifndef abstract_h
#define abstract_h
#include "abstract_blink.h"
#endif
#include "my_first_blinker.h"
abstract_blink* myAbstractBlinker; // a pointer to our abstract class
my_first_blinker myBlinkerImplemenation; // an implementation of the abstract class


Then in `void setup()` we can set myAbstractBlinker to myBlinkerImplementation

void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
  myAbstractBlinker = &myBlinkerImplemenation; // tell the abstract class to point to the address of myBlinkerImlemention
}

Finally, we can call doBlink on the abstract class, and it will perform the function we made in the implementation.

// the loop function runs over and over again forever
void loop() {
  myAbstractBlinker->doBlink();
}


Ok, that was a lot of work - and for what?

In the real world, you wouldn't do this with this example. However, if you're doing something more complicated like, say, setting up some animations for a neo-pixel light display, you might want to use abstraction to make your animations. 
In this example, let's say we wanted do something like press a button, and have the light blink more slowly.
Well, now you just need to make a new .h and .cpp file that inherits your abstraction and in the doBlink() function of the new class, you change what it does. For instance, you can make the lights blink more slowly.  Then in your main function, when the button is pressed, you set the address of myAbstractBlinker to point to the address of your new implementation. 

Something like this:

void onButtonPress(){
  myAbstractBlinker = &mySlowBlinker;
}


Now you an create a bunch of 'scenes', and swap them out however you'd like. If you want to change one, then all of the code for that 'scene' is in the associated cpp file. Give it a shot and you might find a whole new way to organize your code and make your arduino do some cool stuff.

Good Luck, and Happy Coding!
-Tom. 














 





  


Comments

Popular posts from this blog

Using GIT with Arduino

Programming Arduino with Regular Expressions

Organize your Arduino code with header and class files