Arduino Object Oriented Programming (OOP)

This tutorial is an introduction to Arduino Object Oriented Programming. If you’re already programming using C++ and OOP, and want to start writing Arduino OOP code, you’re in the right place.

I’ll show you through some examples how to re-write some of the most common Arduino tutorials using the OOP way.

At the end of the tutorial you’ll get the complete code so you can directly test on your own Arduino board.

Alright, let’s get started!

Arduino Object Oriented Programming limitations

Even if Oriented Object Programming is possible with Arduino, you have to know the limitations.

Basically, the Arduino language is a subset of C/C++. You can create classes, use inheritance, composition, and many other nice OOP functionalities, but:

  • The STL library is not available (not natively, you can still use an external library to get most of the functionalities).
  • You can’t use exceptions.
  • You can’t use “new” and “delete” keywords. This is one of the main OOP limitations you need to be aware of.

To create an object you’ll have to write Object object;, not Object object = new Object();.

Hardware setup

Note: it’s OK if you don’t have any Arduino or hardware component available. You can still follow this tutorial and get all the knowledge you need.

We’ll be using the following hardware setup:

Arduino schematics - 4 LEDs and a button

Component list:

  • 1* Arduino Uno board (Any Arduino board will do)
  • 1* breadboard
  • 4* LEDs (any color you want)
  • 4* 220 Ohm resistors (one for each LED)
  • 1* push button
  • 1* 10k Ohm resistor (for the push button)
  • A few male-to-male wires

The goal of the tutorial will be to turn on LED 1 & 3, and turn off LED 2 & 4 when the button is pressed. When the button is not pressed we’ll do the opposite – turn off LED 1 & 3, and turn on LED 2 & 4.

Now let’s go step by step to reach this goal.

A class for a LED

A LED is a very basic component. You setup a digital pin to INPUT mode, and then you just need to set its state to HIGH or LOW.

Let’s create a simple class to wrap the LED functionalities.

class Led {
  private:
    byte pin;
  public:
    Led(byte pin) {
      // Use 'this->' to make the difference between the
      // 'pin' attribute of the class and the 
      // local variable 'pin' created from the parameter.
      this->pin = pin;
      init();
    }

    void init() {
      pinMode(pin, OUTPUT);
      // Always try to avoid duplicate code.
      // Instead of writing digitalWrite(pin, LOW) here,
      // call the function off() which already does that
      off();
    }

    void on() {
      digitalWrite(pin, HIGH);
    }

    void off() {
      digitalWrite(pin, LOW);
    }
}; // don't forget the semicolon at the end of the class

You can now use this class to create a Led object.

#define LED_1_PIN 9

class Led {
    // class definition
};

Led led1(LED_1_PIN);

void setup() { }

void loop() {
    led1.on();
    delay(500);
    led.off();
    delay(500);
}

With the Led class we can hide all the Arduino stuff about digital pins. Note that the object must be created in the global scope if you want to be able to use it in the setup() and loop() functions.

Class for a Button

Let’s now write some OOP code for the button! The button is a little bit more complex, because we need to add a debounce functionality if we want it to remove the mechanical noise.

class Button {
  private:
    byte pin;
    byte state;
    byte lastReading;
    unsigned long lastDebounceTime = 0;
    unsigned long debounceDelay = 50;
  public:
    Button(byte pin) {
      this->pin = pin;
      lastReading = LOW;
      init();
    }

    void init() {
      pinMode(pin, INPUT);
      update();
    }

    void update() {
      // You can handle the debounce of the button directly
      // in the class, so you don't have to think about it
      // elsewhere in your code
      byte newReading = digitalRead(pin);
      
      if (newReading != lastReading) {
        lastDebounceTime = millis();
      }

      if (millis() - lastDebounceTime > debounceDelay) {
        // Update the 'state' attribute only if debounce is checked
        state = newReading;
      }

      lastReading = newReading;
    }

    byte getState() {
      update();
      return state;
    }

    bool isPressed() {
      return (getState() == HIGH);
    }

}; // don't forget the semicolon at the end of the class

Here again, all the complexity is hidden. Once you’ve implemented the debounce functionality for the button inside the class, you don’t need to think about it anymore.

In your program, you just need to create a Button object and check whether it’s pressed or not.

#define LED_1_PIN 9
#define BUTTON_PIN 5

class Led {
    // class definition
};

class Button {
    // class definition
};

Led led1(LED_1_PIN);
Button button1(BUTTON_PIN);

void loop() {
  if (button1.isPressed()) {
    led1.on();
  }
  else {
    led1.off();
  }
}

When the button is pressed, we turn on LED 1. As you can see the code in the loop() is quite small and clean.

Complete Arduino Object Oriented code

OOP is great for reusability. Do you remember we added 4 LEDs at the beginning of the tutorial? Well, now that we have a class for a LED, we just need to create additional objects, all the implementation is already done.

Here is the complete code, including the Led class, the Button class, and the main code of the program.

#define LED_1_PIN 9
#define LED_2_PIN 10
#define LED_3_PIN 11
#define LED_4_PIN 12

#define BUTTON_PIN 5

class Led {
  private:
    byte pin;
  public:
    Led(byte pin) {
      // Use 'this->' to make the difference between the
      // 'pin' attribute of the class and the 
      // local variable 'pin' created from the parameter.
      this->pin = pin;
      init();
    }

    void init() {
      pinMode(pin, OUTPUT);
      // Always try to avoid duplicate code.
      // Instead of writing digitalWrite(pin, LOW) here,
      // call the function off() which already does that
      off();
    }

    void on() {
      digitalWrite(pin, HIGH);
    }

    void off() {
      digitalWrite(pin, LOW);
    }
}; // don't forget the semicolon at the end of the class

class Button {
  private:
    byte pin;
    byte state;
    byte lastReading;
    unsigned long lastDebounceTime = 0;
    unsigned long debounceDelay = 50;
  public:
    Button(byte pin) {
      this->pin = pin;
      lastReading = LOW;
      init();
    }

    void init() {
      pinMode(pin, INPUT);
      update();
    }

    void update() {
      // You can handle the debounce of the button directly
      // in the class, so you don't have to think about it
      // elsewhere in your code
      byte newReading = digitalRead(pin);
      
      if (newReading != lastReading) {
        lastDebounceTime = millis();
      }

      if (millis() - lastDebounceTime > debounceDelay) {
        // Update the 'state' attribute only if debounce is checked
        state = newReading;
      }

      lastReading = newReading;
    }

    byte getState() {
      update();
      return state;
    }

    bool isPressed() {
      return (getState() == HIGH);
    }
}; // don't forget the semicolon at the end of the class

// Create your objects in the global scope so you can
// get access to them in the setup() and loop() functions
Led led1(LED_1_PIN);
Led led2(LED_2_PIN);
Led led3(LED_3_PIN);
Led led4(LED_4_PIN);
Button button1(BUTTON_PIN);

void setup() { }

void loop() {
  if (button1.isPressed()) {
    led1.on();
    led2.off();
    led3.on();
    led4.off();
  }
  else {
    led1.off();
    led2.on();
    led3.off();
    led4.on();
  }
}

Reorganize your Arduino OOP code

The previous code works well, but everything is in the same file. We want to use OOP for reusability, modularity, readability, etc, but it’s impossible if we write all the code in one file. As your program grows in complexity, so your code length, until you reach a point where the code is so long that you spend more time finding stuff and fixing bugs instead of adding new functionalities.

We’ll separate the code into 3 parts: the Led class, the Button class, and the “main”.

Each class will be on its own independent file. In fact, for one class we’ll have 2 files: one Cpp file (.cpp) and one header file (.h). Your code will become much more readable. The classes you create will be also more reusable as you can include them in every file where you need them.

Note that creating other files for an Arduino program is quite tricky. You must create all your files inside your Arduino program folder. Example: if your program is named Test.ino, then it will be automatically saved on a Test/ folder (the Arduino IDE does that). You’ll have to put all your files in the Test/folder as well, so the Arduino IDE can find them.

Go into the folder of your current Arduino program. Create 4 files:

  • Led.h
  • Led.cpp
  • Button.h
  • Button.cpp

The files won’t appear in the Arduino IDE right away. You’ll need to close the project, and open it again so the files will show in tabs on the top.

Arduino OOP multiple files

Here is the complete code for all 5 files. The classes and functionalities are exactly the same as the code we just wrote before.

Led.h

#include <Arduino.h>

class Led {
  
  private:
    byte pin;
    
  public:
    Led(byte pin);
    void init();
    void on();
    void off();
};

In this file we just write the class declaration, which is the interface that any client using this class will use. This makes the class much easier to understand and use.

Note that I’ve added #include <Arduino.h> at the beginning. Why? This include is necessary to use the specific Arduino functions and types (think of pinMode(), digitalWrite(), byte). In the “main” file we don’t need to write it because it’s automatically added when you compile your code. But in any other file, you need to add it by yourself.

Led.cpp

#include "Led.h"

Led::Led(byte pin) {
  // Use 'this->' to make the difference between the
  // 'pin' attribute of the class and the 
  // local variable 'pin' created from the parameter.
  this->pin = pin;
  init();
}

void Led::init() {
  pinMode(pin, OUTPUT);
  // Always try to avoid duplicate code.
  // Instead of writing digitalWrite(pin, LOW) here,
  // call the function off() which already does that
  off();
}

void Led::on() {
  digitalWrite(pin, HIGH);
}

void Led::off() {
  digitalWrite(pin, LOW);
}

Button.h

#include <Arduino.h>

class Button {
  
  private:
    byte pin;
    byte state;
    byte lastReading;
    unsigned long lastDebounceTime = 0;
    unsigned long debounceDelay = 50;
    
  public:
    Button(byte pin);

    void init();
    void update();

    byte getState();
    bool isPressed();
};

Same warning as for the Led.h file. Don’t forget to include the Arduino library at the beginning of the file.

Button.cpp

#include "Button.h"

Button::Button(byte pin) {
  this->pin = pin;
  lastReading = LOW;
  init();
}

void Button::init() {
  pinMode(pin, INPUT);
  update();
}

void Button::update() {
    // You can handle the debounce of the button directly
    // in the class, so you don't have to think about it
    // elsewhere in your code
    byte newReading = digitalRead(pin);
    
    if (newReading != lastReading) {
      lastDebounceTime = millis();
    }

    if (millis() - lastDebounceTime > debounceDelay) {
      // Update the 'state' attribute only if debounce is checked
      state = newReading;
    }

    lastReading = newReading;
}

byte Button::getState() {
  update();
  return state;
}

bool Button::isPressed() {
  return (getState() == HIGH);
}

Main

#include "Led.h"
#include "Button.h"

#define LED_1_PIN 9
#define LED_2_PIN 10
#define LED_3_PIN 11
#define LED_4_PIN 12

#define BUTTON_PIN 5

Led led1(LED_1_PIN);
Led led2(LED_2_PIN);
Led led3(LED_3_PIN);
Led led4(LED_4_PIN);
Button button1(BUTTON_PIN);

void setup() { }

void loop() {
  if (button1.isPressed()) {
    led1.on();
    led2.off();
    led3.on();
    led4.off();
  }
  else {
    led1.off();
    led2.on();
    led3.off();
    led4.on();
  }
}

Well, as you can see, the code is now much clearer and readable. Plus, you could import the Led and Button class in any other part of your application.

For example you could create a class named LedPanel. This class would contain an array of Led objects and handle them. In your “main”, you could just import the LedPanel header file without having to know about the Led class. In fact, why don’t you try to do that to practice more?

You can always create more modules on top of other modules. That’s the power of Object Oriented Programming with Arduino (and not only Arduino by the way).

Arduino Object Oriented: it’s already everywhere

If it’s the first time you use Object Oriented Programming with Arduino, well… Don’t think you’re doing something new! In fact, many of the Arduino already use OOP.

A few OOP library examples:

  • Servo: control a servo motor
  • Serial: communicate between your Arduino board and other devices
  • Wire: communicate though the I2C protocol
  • SevSeg: control a digit 7-segment display
  • EEPROM: store values permanently in your Arduino board

If you’re already familiar with OOP and want to use it in your Arduino programs, don’t hesitate and do it! OOP is certainly not the answer to everything, but if you know how to use it right, there is no reason you couldn’t get its benefits with Arduino.

1 thought on “Arduino Object Oriented Programming (OOP)”

Leave a Comment