A while back I wrote a tutorial for writing a library to blink an LED, sort of like your "LedOnOff()" function but with several functions. This part explains why you will want to write an object class instead of one function:
All-together you have:
Main Sketch:
#include "BlinkLib.h"
void setup()
{
setPin(LED_BUILTIN);
}
void loop()
{
blink(1000);
}
Library header:
void blink(unsigned long);
void setPin(int);
Library source:
#include <Arduino.h>
#include "BlinkLib.h"
int ledState = LOW;
unsigned long previousMillis = 0;
int ledPin = LED_BUILTIN;
void setPin(int pin)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
}
void blink(unsigned long interval)
{
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}
Ah, but there is one small problem. Now you want to blink multiple LEDs at different rates! There is only one global "ledPin" variable so if you call "setPin()" a second time, the first pin number is replaced by the second pin number, and blink() will only blink the second pin. Also, the "ledState" and "previousMillis" globals are shared among the functions.
To fix that, we are going to 'encapsulate' the functionality of the library into an 'object'. You can then make multiple 'instances' of the object, each with its own variables.
Let's create an object class named 'Blinker'. This goes into BlinkLib.h in place of the shared function prototypes.
class Blinker
{
public:
Blinker(const byte pin); // "Constructor" called when the object is defined
void blink(const unsigned long interval);
private:
const byte ledPin;
int ledState;
unsigned long previousMillis;
};
Now we can change the test sketch to blink two LEDs:
#include "BlinkLib.h"
Blinker blinker(LED_BUILTIN); // Blink the built-in LED
Blinker blinker2(9); // Blink the LED on Pin 9
void setup()
{
}
void loop()
{
blinker.blink(1000);
blinker2.blink(750);
}
We can try to Verify the test sketch but we have not defined the functions ("member functions" or "methods") declared in the class yet. We get undefined reference errors for Blinker::Blinker() (the constructor) and Blinker::blink() (the function that does stuff!). The second one is easy to fix because we just have to change the declaration of 'blink' from:
void blink(unsigned long interval)
to
void Blinker::blink(unsigned long interval)
That tells the compiler that we are not defining just a function, but a function that is a member ("member function" or "method") of the object class "Blinker". Nothing else about 'blink()' has to be changed because the blink() "method" is perfectly happy to use the "member variables" of its class in place of the old globals. We can remove the globals and the setPin() function from BlinkLib.cpp because they are no longer used:
#include <Arduino.h>
#include "BlinkLib.h"
void Blinker::blink(unsigned long interval)
{
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}
Now that "Blinker::blink()" is taken care of, we need to define Blinker::Blinker(). This is the "constructor": the function automatically called when an object of this class (an "instance") is created ("instantiated"). It is a good place to do some initialization. We can put this function in BlinkLib.cpp:
Blinker::Blinker(const byte pin)
{
ledPin = pin;
ledState = LOW;
previousMillis = 0;
pinMode(ledPin, OUTPUT);
previousMillis = millis();
}
The only problem is, "ledPin" is declared with the keyword 'const' so it can't be assigned a value inside the constructor. It has to be given a value BEFORE the body of the constructor so we have to use some special syntax:
Blinker::Blinker(const byte pin) : ledPin(pin)
{
ledState = LOW;
previousMillis = 0;
pinMode(ledPin, OUTPUT);
previousMillis = millis();
}
Yay! The sketch compiles without any errors or warnings!
Except for one thing. A thing the compiler can't warn you about because it doesn't know. The Arduino library isn't initialized until just before your setup() function is called. That is AFTER all of the global variables are initialized. That includes calling the constructor of every global object instance. In other words, your object class can't safely call any Arduino functions (like "pinMode()" or "millis()") in the constructor! You may have wondered why some Arduino library objects have a 'begin()' method that you have to call from setup(). That's why.
To make our object safe to use we add a 'begin()' method (the name is just an Arduino convention) and call the Arduino functions from there.
void Blinker::begin()
{
pinMode(ledPin, OUTPUT);
previousMillis = millis();
}
That goes in BlinkLib.cpp and we call it in setup().
We have to add the "void begin();" declaration to the class (in BlinkLib.h) and we're ready to go!
All-together you have:
Main Sketch:
#include "BlinkLib.h"
Blinker blinker(LED_BUILTIN); // Blink the built-in LED
Blinker blinker2(9); // Blink the LED on Pin 9
void setup()
{
blinker.begin();
blinker2.begin();
}
void loop()
{
blinker.blink(1000);
blinker2.blink(750);
}
Library header:
class Blinker
{
public:
Blinker(const byte pin); // "Constructor" called when the object is defined
void begin(); // A function to call to do initialization
void blink(const unsigned long interval);
private:
const byte ledPin;
int ledState;
unsigned long previousMillis;
};
Library source:
#include <Arduino.h>
#include "BlinkLib.h"
Blinker::Blinker(const byte pin) : ledPin(pin)
{
ledState = LOW;
previousMillis = 0;
}
void Blinker::begin()
{
pinMode(ledPin, OUTPUT);
previousMillis = millis();
}
void Blinker::blink(unsigned long interval)
{
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}