Skeleton Sketch

Tutorial

Skeleton Sketch

Like most volunteers I try to offer programming solutions that are written at an introductory level.

As an example, we respond to new users use of delay( ) by recommending a delay based on the millis( ) or micros( ) functions.

When it comes to handling switches, we suggest looking at when an input changes state rather than the state of the input.

When it comes to sequential code, we offer the user to use a State Machine.

At some point in our programming, we leave behind basic techniques and advance to more powerful C++ methods.

As with many here, I have different Skeleton Templates that I use when starting out on a new project.

Attached below is one template version I use.

This version contains most of the normal sections often found in a sketch.

I start with deleting those sections that will not be needed then flesh out the remaining code to accomplish my task.

As a summary, this Skeleton Sketch has the following sections:
• a class that handles digital inputs
• a class that creates TIMER objects
• diagnostic code to help in debugging
• a basic State Machine
• a code block that handles I2C and parallel LCDs
• a list of often used macros

I offer this version of the skeleton as an example to new users so it might aid in their programming.

I should point out this is a work in progress and just as with all other software is always in flux; as an example, I recently added pause and release capability to the TIMER class which can pause a TIMER when you need more time allocation to handle a situation. You can then release the TIMER when normal code operation can return.

New users feel free to add or subtract to make this your own tool.

If others have suggestions or comments, feel free to mention them here.

In this example, all items are in one .ino file. You would of course not usually do this.

Note:

Normally we organize our code source in one of two ways:
• moving a class into its own library folder
• move class items and things like macros and other often used pieces of code into a separate folder where the .ino folder is located.
We might call this folder Header.h then access it with #include “Header.h” at the top of the .ino file.

//
//
//  ___BasicSkeleton.ino
//
//
//  Version    YY/MM/DD    Comments
//  =======    ========    ====================================================================
//  1.00       23/02/13    Running code
//
//
//

//include macros class definitions and other items
//#include "Header.h"

//
//
//                                       M a c r o s
//********************************************^************************************************
//
#define EXPIRED            true
#define stillTIMING        false

#define ENABLED            true
#define DISABLED           false

#define LEDon              HIGH    //+5V---[220R]---[LED]---PIN
#define LEDoff             LOW

#define OPENED             HIGH    //+5V---[Internal 50k]---PIN---[Switch]---GND  
#define CLOSED             LOW

//#define OPENED           LOW     //+5V---[Switch]---PIN---[10k]---GND
//#define CLOSED           HIGH


//                             F o r   d i a g n o s t i c s
//********************************************^************************************************
// Atmega328: UNO, ProMini or Nano
//
// Note:
// PIND D0 to D7
// D0=0x01 D1=0x02 D2=0x04 D3=0x08 D4=0x10 D5=0x20 D6=0x40 D7=0x80
// PIND0   PIND1   PIND2   PIND3   PIND4   PIND5   PIND6   PIND7
//
// PINB D8 to D13
// D8=0x01 D9=0x02 D10=0x04 D11=0x08 D12=0x10 D13=0x20
// PINB0   PINB1   PINB2    PINB3    PINB4    PINB5

// PINC D14 to D19
// D14=0x01 D15=0x02 D16=0x04 D17=0x08 D18=0x10 D19=0x20
// PINC0    PINC1    PINC2    PINC3    PINC4    PINC5
//
//Macro definition for generating a 63ns pulse on UNO pin 13 i.e. PINB5
//
//add this line to setup()
//pinMode(13,OUTPUT);
//
//if used: PULSE62_13;
//
#define PULSE62_13       cli(); PINB = bit(PINB5); PINB = bit(PINB5); sei()


//********************************************^************************************************
//                       S e r i a l   O R   P a r a l l e l   L C D   ?
//********************************************^************************************************

//uncomment the next line if you have a serial LCD                                <-------<<<<<
#define serialLCDisBeingUsed

//****************************************
#ifdef  serialLCDisBeingUsed

#include <Wire.h>

//Use I2C library:     https://github.com/duinoWitchery/hd44780
//LCD Reference:       https://www.arduino.cc/en/Reference/LiquidCrystal

#include <hd44780.h>   //main hd44780 header

//NOTE:
//hd44780_I2Cexp control LCD using I2C I/O expander backpack (PCF8574 or MCP23008)
//hd44780_I2Clcd control LCD with native I2C interface (PCF2116, PCF2119x, etc...)

#include <hd44780ioClass/hd44780_I2Cexp.h> //I2C expander i/o class header

//If you do not know what your I2C address is, first run the "I2C_Scanner" sketch
//OR
//run the "I2CexpDiag" sketch that comes with the hd44780 library
//hd44780_I2Cexp lcd(0x3F);

hd44780_I2Cexp lcd(0x27);

//****************************************
#else

#include <LiquidCrystal.h>

// LCD pin         4   6  11  12  13  14
//                RS  EN  D4  D5  D6  D7
LiquidCrystal lcd( 4,  5,  6,  7,  8,  9);

#endif


//                     c l a s s   S w i t c h M a n a g e r V e r 2
//********************************************^************************************************
//Class for managing switch presses
//Author: Nick Gammon                      http://gammon.com.au/Arduino/SwitchManager.zip
//Date: 18 December 2013
//Modified: 12 February 2015 to pass pin number to function
//
//Modified by LarryD
//

/*
  Example:
  #include <SwitchManager.h>
  SwitchManager mySwitch;  // declare
  //- newState will be LOW or HIGH (the is the state the switch is now in)
  //- interval will be how many mS between the opposite state and this one
  //- whichPin will be which pin caused this change
  //- you can share the function amongst multiple Switches

  void handleSwitchPress (const byte newState, const unsigned long interval, const byte whichPin)
   {
   }

  void setup ()
   {
   mySwitch.begin (2, handleSwitchPress);
   }

  void loop ()
   {
   mySwitch.check ();  // check for presses
   }
*/

//when needed
//#include <Arduino.h>

//****************************************
class SwitchManagerVer2
{
    enum { debounceTime = 5, noSwitch = -1 };
    typedef void (*handlerFunction) (const byte newState,
                                     const unsigned long interval,
                                     const byte whichSwitch);
    int pinNumber_;
    handlerFunction f_;
    byte oldSwitchState_;
    unsigned long switchPressTime_;  //when the switch last changed state
    unsigned long lastLowTime_;
    unsigned long lastHighTime_;

  public:

    //****************************************
    //constructor
    SwitchManagerVer2()
    {
      pinNumber_ = noSwitch;
      f_ = NULL;
      oldSwitchState_  = HIGH;
      switchPressTime_ = 0;
      lastLowTime_  = 0;
      lastHighTime_ = 0;
    }

    //****************************************
    void begin (const int pinNumber, handlerFunction f)
    {
      pinNumber_ = pinNumber;
      f_ = f;
      if (pinNumber_ != noSwitch)
        pinMode (pinNumber_, INPUT_PULLUP);
    }  //end of begin

    //****************************************
    void check ()
    {
      //we need a valid pin number and a valid function to call
      if (pinNumber_ == noSwitch || f_ == NULL)
      {
        return;
      }

      //*****************
      //debounce
      if (millis () - switchPressTime_ >= debounceTime)
      {
        //time when we closed the switch
        switchPressTime_ = millis ();

        //see if switch is open or closed
        byte switchState = digitalRead (pinNumber_);

        //*****************
        //has it changed since last time ?
        if (switchState != oldSwitchState_)
        {
          //remember for next time
          oldSwitchState_ =  switchState;

          //*****************
          if (switchState == LOW)
          {
            lastLowTime_ = switchPressTime_;
            f_ (LOW, lastLowTime_ -  lastHighTime_, pinNumber_);
          }

          //*****************
          else
          {
            lastHighTime_ = switchPressTime_;
            f_ (HIGH, lastHighTime_ - lastLowTime_, pinNumber_);
          }

        }  //END of state change
      }  //END if debounce time up
    }  //END of   check()

};  //END of   class SwitchManager


//                              c l a s s    M a k e T i m e r
//********************************************^************************************************
//a class to create TIMER objects

class MakeTimer
{
#define MILLIS        true
#define MICROS        false

#define ENABLED       true
#define DISABLED      false

#define YES           true
#define NO            false

    //********************************************^************************************************
    //  TIMER example:
    //  give this TIMER a name "myTimer"
    //
    //  MakeTimer myTimer =
    //  {
    //     2000ul,       //.interval      (unsigned long)
    //     MILLIS,       //.timerType     (MILLIS or MICROS)
    //     NO,           //.restart       (YES or NO)
    //     DISABLED,     //.enableFlag    (ENABLED or DISABLED)
    //     YES        ;  //.suspendable   (YES or NO) 
    //  };
    
    //  This TIMER has:  
    //  interval of 2 seconds, millis() based TIMER, it does not restart, it is disabled, and it can be suspended 
    //
    //  TIMERs can be paused or released if they have their ".suspendable" member set to YES (true).
    //  You can use this ability to "pause" a group of TIMERs when you need high speed processing and do not want other
    //  events from slowing code execution. When finised, you can "release" the group of paused TIMERs.
    //
    //  N o t e: with delays of < 2 millis, use micros() "MICROS" and adjust "interval" as needed <-----<<<<
    //
    //You have access to:
    //Variables:  myTimer.interval, myTimer.timerType, myTimer.restart, myTimer.enableFlag, myTimer.suspendable
    //
    //Functions: myTimer.checkTimer(), myTimer.isEnabled(), myTimer.enableTimer(), myTimer.disableTimer(),
    //           myTimer.expireTimer(), MakeTime::pauseTimers(), MakeTime::releaseTimers(), myTimer.restartTimer()

  private:
    unsigned long previousTime = 0;
    unsigned long currentTime  = 0;

    //static variables must be initialized after the class definition (i.e. bool MakeTimer::pauseFlag = false;)
    static bool   pauseFlag;      //if true, TIMERs which are "suspendable" are paused

  public:
    //these 5 "members" are needed to define a TIMER
    unsigned long interval;       //delay time (ms/us) which we are looking for
    bool          timerType;      //what kind of TIMER is this, MILLIS or MICROS
    bool          restart;        //do we start this TIMER again and again, YES or NO
    bool          enableFlag;     //tells us if this TIMER is ENABLED or DISABLED
    bool          suspendable;    //can this TIMER be suspended ? YES or NO

    //****************************************
    //default constructor
    MakeTimer()
    {
      //do nothing
    }

    //****************************************
    //constructor with parameters
    MakeTimer(unsigned long interval_, bool timerType_, bool restart_, bool enableFlag_, bool suspendFlag_)
    {
      interval    = interval_;
      timerType   = timerType_;
      restart     = restart_;
      enableFlag  = enableFlag_;
      suspendable = suspendFlag_;
    }

    //****************************************
    //Function to check if this TIMER has expired ex: myTimer.checkTimer();
    bool checkTimer()
    {
      //*********************
      //is this a suspendable TIMER and is it paused ?
      if (suspendable == YES && pauseFlag == true)
      {
        //this TIMER is paused
        return false;
      }

      currentTime = getTime();

      //*********************
      //has this TIMER expired ?
      if (currentTime - previousTime >= interval)
      {
        //*********************
        //should this TIMER start again?
        if (restart == ENABLED)
        {
          //get ready for the next iteration
          previousTime = currentTime;
        }

        //this TIMER has expired
        return true;
      }

      //this TIMER has not expired
      return false;
      
    } //END of   checkTimer()


    //****************************************
    //Function to see if this TIMER is enabled, ex: myTimer.isEnabled();
    bool isEnabled()
    {
      return enableFlag;

    } //END of   isEnabled()

    //****************************************
    //Function to "enable" and "restart" this TIMER, ex: myTimer.enableTimer();
    void enableTimer()
    {
      enableFlag = ENABLED;

      //initialize previousTime to current millis() or micros()
      previousTime = getTime();

    } //END of   enableTimer()

    //****************************************
    //Function to disable this TIMER, ex: myTimer.disableTimer();
    void disableTimer()
    {
      enableFlag = DISABLED;

    } //END of   disableTimer()

    //****************************************
    //Function to force this TIMER to expire ex: myTimer.expireTimer();
    void expireTimer()
    {
      //force this TIMER to expire
      previousTime = getTime() - interval;

    } //END of   expireTimer()

    //****************************************
    //Function to pause suspendable TIMERs  ex: MakeTimer::pauseTimers();
    static void pauseTimers()
    {
      pauseFlag = ENABLED;

    } //END of   pauseTimers()

    //****************************************
    //Function to release suspendable TIMERs  ex: MakeTimer::releaseTimers();
    static void releaseTimers()
    {
      pauseFlag = DISABLED;

    } //END of   releaseTimers()

    //****************************************
    //Function to restart this TIMER ex: myTimer.restartTimer();
    void restartTimer()
    {
      //restart this TIMER
      previousTime = getTime();

    } //END of   restartTimer()

    //****************************************
    //Function to return the current time
    unsigned long getTime()
    {
      //return the time             i.e. the value returned from millis() or micros()
      //*********************
      if (timerType == MILLIS)
      {
        return millis();
      }

      return micros();

    } //END of   getTime()

}; //END of   class “MakeTimer”

//****************************************
//initialize static member pauseFlag of class MakeTimer
bool MakeTimer::pauseFlag = false;



//                                T I M E R   o b j e c t s
//********************************************^************************************************
//make our "TIMER" objects
//these 5 "members" are needed to define a TIMER
//
//interval     delay time (ms/us) which we are looking for
//timerType    what kind of TIMER is this, MILLIS or MICROS
//restart      do we start this TIMER again and again, YES or NO
//enableFlag   tells us if this TIMER is ENABLED or DISABLED
//suspendable  can this TIMER be suspended ? YES or NO

//****************************************
MakeTimer heartbeatTIMER =
{
  500ul,    //.interval    (an unsigned long value)
  MILLIS,   //.timerType   (MILLIS or MICROS)
  YES,      //.restart     (YES or NO)
  ENABLED,  //.enabledFlag (ENABLED or DISABLED)
  YES       //.suspendable (YES or NO)
};

//****************************************
MakeTimer stateMachineTIMER =
{
  1000ul,   //.interval    (an unsigned long value)
  MICROS,   //.timerType   (MILLIS or MICROS)
  YES,      //.restart     (YES or NO)
  ENABLED,  //.enabledFlag (ENABLED or DISABLED)
  NO        //.suspendable (YES or NO)
};

//****************************************
MakeTimer commonTIMER =
{
  200ul,    //.interval    (an unsigned long value)
  MILLIS,   //.timerType   (MILLIS or MICROS)
  YES,      //.restart     (YES or NO)
  DISABLED, //.enabledFlag (ENABLED or DISABLED)
  NO        //.suspendable  (YES or NO)
};


//                          S t a t e   M a c h i n e   S t u f f
//********************************************^************************************************
//define the "states" in our State Machine

enum SM {Startup, State1, State2, State3};

SM machineState = Startup;


//                        s w i t c h   i n s t a n t i a t i o n s
//********************************************^************************************************
//make our "Switch" objects

SwitchManagerVer2 toggleLEDswitch;


//                              G P I O   &   V a r i a b l e s
//********************************************^************************************************
//
const byte toggleLEDswitchPin         = 2;          //+5V---[Internal 50k]---PIN---[Switch]---GND

const byte testLED                    = 3;          //+5V---[220R]---[LED]---PIN
//pins used for a parallel LCD  4, 5, 6, 7, 8, 9
const byte machineTestLED             = 12;
const byte heartbeatLED               = 13;

//****************************************
byte counter;


//                                       s e t u p ( )
//********************************************^************************************************
//
void setup()
{
  Serial.begin(115200);

  pinMode(testLED, OUTPUT);
  pinMode(machineTestLED, OUTPUT);
  pinMode(heartbeatLED, OUTPUT);

  //used for diagnostics                                                          <-------<<<<<
  //pinMode(13, OUTPUT);  //in this case pin 13 is the heartbeat LED

  //****************************************
  //LCD stuff
  lcd.begin(16, 2);
  lcd.clear();

  lcd.setCursor(0, 0);
  //                   111111
  //         0123456789012345
  //         Skeleton  Sketch
  lcd.print("Skeleton  Sketch");

  //****************************************
  //initialize "Switch" object(s)
  toggleLEDswitch.begin (toggleLEDswitchPin, handleSwitches);

  //****************************************
  //initialize/restart TIMERs that are ENABLED at power up time
  heartbeatTIMER.restartTimer();
  stateMachineTIMER.restartTimer();

} //END of   setup()


//                                        l o o p ( )
//********************************************^************************************************
//
void loop()
{
  //****************************************                      D i a g n o s t i c s
  //for diagnostics, print the loop() execution time
  //
  //for oscilloscope measuring
  //two pulses indicate the beginning of loop
  //PULSE62_13;
  //PULSE62_13;
  //
  //print loop() execution time
  //static unsigned long speedTime;
  //Serial.println(micros() - speedTime);
  //speedTime = micros();

  //for oscilloscope measuring, the time when loop's main code actually starts
  //PULSE62_13;

  //****************************************                      h e a r t b e a t T I M E R
  //is it time to toggle the heartbeatLED ?
  if (heartbeatTIMER.checkTimer() == EXPIRED)
  {
    //toggle the heartbeatLED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

  //****************************************                      s t a t e M a c h i n e T I M E R
  //is it time to check the the State Machine ?
  if (stateMachineTIMER.checkTimer() == EXPIRED)
  {
    checkMachine();
  }


  //========================================
  //Other non blocking code goes here
  //========================================


  //****************************************                      c h e c k   s w i t c h e s
  //check "Switches" at non blocking loop() speed !
  //when a "Switch" changes state, function handleSwitches() is called
  //
  toggleLEDswitch.check();

} //END of   loop()


//                               h a n d l e S w i t c h e s ( )
//********************************************^************************************************
//You get here "ONLY" if there has been a valid change in a "Switch's" state (>15ms).

//When a "Switch" has changed state, SwitchManagerWithFilter passes this function 3 arguments:
//"newState"    - this will be HIGH or LOW. This is the state the "Switch" is in now.
//"interval"    - the number of milliseconds the "Switch" stayed in the previous state
//"whichPin"    - this is the "Switch" input pin that we are examining

void handleSwitches (const byte newState, const unsigned long interval, const byte whichPin)
{
  switch (whichPin)
  {
    //****************************************                      t o g g l e L E D s w i t c h P i n
    case toggleLEDswitchPin:
      {
        //*********************             CLOSED
        //did the "Switch" closed ?
        if (newState == CLOSED)
        {
          //toggle the testLED
          digitalWrite(testLED, !digitalRead(testLED));

          Serial.print("Switch was OPEN   for \t");
          Serial.print(interval);
          Serial.println(" ms ");
        }

        //*********************             OPENED
        //the "Switch" OPENED
        else
        {
          Serial.print("Switch was CLOSED for \t");
          Serial.print(interval);
          Serial.println(" ms \n");
        }
      }
      break;                                    //END of   case:

      //****************************************


      //========================================
      //other "Switches" go here
      //========================================

  } //END of   switch/case

} //END of   handleSwitches()


//                                c h e c k M a c h i n e ( )
//********************************************^************************************************
//
void checkMachine()
{
  //****************************************
  //service the "current" Machine State
  switch (machineState)
  {
    //*********************
    case Startup:
      {
        //do startup stuff

        lcd.setCursor(0, 1);
        //                   111111
        //         0123456789012345
        //           Counter = xx
        lcd.print("  Counter =     ");

        //set the commonTIMER interval to 200ms
        commonTIMER.interval = 200ul;

        commonTIMER.enableTimer();

        //next state
        machineState = State1;
      }
      break;                                    //END of   case:

    //************************
    case State1:
      {
        //if enabled, is the commonTIMER expired ?
        if (commonTIMER.isEnabled() && commonTIMER.checkTimer() == EXPIRED)
        {
          //toggle the machineTestLED
          digitalWrite(machineTestLED, !digitalRead(machineTestLED));

          if (counter++ > 49)
          {
            counter = 0;
            commonTIMER.interval = 500;

            lcd.setCursor(0, 1);
            //                   111111
            //         0123456789012345
            //           Counter = xx
            lcd.print("  Counter =     ");

            machineState = State2;
          }

          lcd.setCursor(12, 1);
          //                   111111
          //         0123456789012345
          //           Counter = xx
          lcd.print(counter);

          //release all suspendable TIMERs
          MakeTimer::releaseTimers();
        }

        //none commonTIMER code
      }
      break;                                    //END of   case:

    //************************
    case State2:
      {
        //if enabled, is the commonTIMER expired ?
        if (commonTIMER.isEnabled() && commonTIMER.checkTimer() == EXPIRED)
        {
          //toggle the machineTestLED
          digitalWrite(machineTestLED, !digitalRead(machineTestLED));

          if (counter++ > 49)
          {
            counter = 0;
            commonTIMER.interval = 200;

            lcd.setCursor(0, 1);
            //                   111111
            //         0123456789012345
            //           Counter = xx
            lcd.print("  Counter =     ");

            machineState = State1;
          }

          lcd.setCursor(12, 1);
          //                   111111
          //         0123456789012345
          //           Counter = xx
          lcd.print(counter);

          //pause all suspendable TIMERs
          MakeTimer::pauseTimers();
        }

        //none commonTIMER code
      }
      break;                                    //END of   case:

    //************************
    case State3:
      {
        //do something
      }
      break;                                    //END of   case:

  } //END of  switch/case

} //END of   checkMachine()


//********************************************^************************************************
//                                E N D   O f   S k e t c h
//********************************************^************************************************

Accompanying schematic:

4 Likes

Combined with a hardware POST...

BANK!

Who are your intended target users? In this Arduino Forum, there are users from a novice with no programming experience at all up to veterans.

What is the Arduino Platform -- is it Arduino UNO?

Hello LarryD

Thank you very much for the work you have done with this tutorial. :+1:
This tutorial is excellent and the best I have seen for making the transition from procedural to object oriented programming.

Have a nice day and enjoy coding in C++.

Good points.

This is meant for those individuals who are curious about taking their C++ experiment from basic coding to OOP and who want to learn techniques that can solve common pitfalls which new users might fall into.

As for the platform it is buried in the comments, the debugging aspect relies on having an Atmega 328 Arduino.

I will add a schematic to the opening post to aid in hardware connections.

Of course as with everything, just trying to teach how to fish.

1 Like

Would appreciate to see the Pictorial View of the particular Arduino UNO Board to which U2 of the schematic of post #1 belongs to. This is to clarify few confusions as to the Serial Number-1 to 32, VIN, open GND, etc.

1 Like

If you have the schematic of the UNO Board of post #7, please post it.

1 Like

Would appreciate to see class based codes based on the template of post #1 of @LarryD for the following procedural codes to blink LED-D2 (schematic of post #1) at 1-sec interval.

void setup() 
{
 pinMode(13, OUTPUT);
}

void loop() 
{
 digitalWrite(13, HIGH);
 delay(1000);
 digitalWrite(13, LOW);
 delay(1000);
}

Hello GolamMostafa

Yes, for sure.
Please find below a class-less presentation of a OOP solution wrt youe blink Led example.

Add flashing time data and port pins to these lines of code. This way you can easily extend this example for flashing leds, limited only by the number of free port pins.

constexpr int LedPins[] {LED_BUILTIN};
constexpr unsigned long FlashTimes [][2] {{1000,1000}};

And the sketch Blinking Led using OOP:

/* BLOCK COMMENT
  - Blinking Led using OOP
  - This sketch may contain traces of C++.
  - In case of indisposition:
  - https://www.learncpp.com/
  - Hardware:
  - Thanks to LarryD
  - https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
  - Tested @ Arduino: Mega[X] - UNO [ ] - Nano [ ]
*/
#define ProjectName "Blinking Led using OOP"
// make variables
constexpr int LedPins[] {LED_BUILTIN};
constexpr unsigned long FlashTimes [][2] {{1000,1000}};
// make structure
struct LEDBLINK {
  int pin;
  unsigned long interval[2];
  unsigned long stamp;
  void begin ( int pin_, unsigned long intervalLow,unsigned long intervalHigh)
  {
    pin = pin_;
    pinMode(pin, OUTPUT);
    interval[LOW] = intervalLow;
    interval[HIGH] = intervalHigh;
  }
  void run ( unsigned long currentMillis)
  {
    if (currentMillis - stamp >= interval[digitalRead(pin)])
    {
      stamp = currentMillis;
      digitalWrite(pin, digitalRead(pin) ? LOW : HIGH);
    }
  }
};
// instantiations of structures 
LEDBLINK ledBlinks[sizeof(LedPins) / sizeof(LedPins[0])];

void setup()
{
  Serial.begin(115200);
  Serial.print(ProjectName), Serial.print(" in "), Serial.println(__FILE__);
  int number = 0;
  // initialization of structures 
  for (auto &ledBlink : ledBlinks) ledBlink.begin(LedPins[number], FlashTimes[number][LOW],FlashTimes[number][HIGH]), number++;
}
void loop()
{
  unsigned long currentMillis = millis();
  for (auto &ledBlink : ledBlinks) ledBlink.run(currentMillis);
}

Have a nice day and enjoy coding in C++.

Unfortunately, I don't see explicit class declaration using class keyword and the corresponding object and methods in your sketch.

That's right.

You didn't read and understand my explanation of the sketch, did you?

The keyword class is not necessary to programme an OOP program.

The keywords class and struct are similar in use.

Have a nice day and enjoy coding in C++.

Does struct keyword support private and protected keywords?

For sure not.

Do you know the meaning of the the word 'similar'?

The main difference between class and struct:

  • a struct has by default member items as public
  • a class has by default member items as private

However, both can have private or public sections.

@paulpaulson

@LarryD has emphasized on C++ Programming which is based on class keyword.

So, let us use class keyword to create user-defined data type and then declare object on which desired method(s) will be applied to blink LED-D2 at 1-sec interval.

Similar does not mean identical.

Here is a class to do this:


//********************************************^************************************************
class FlashLED
{
  private:
    unsigned long previousTime;

  public:
    unsigned long interval;
    byte pinNumber;

    FlashLED(unsigned long interval_, byte pinNumber_)
    {
      interval = interval_;
      pinNumber = pinNumber_;

      pinMode (pinNumber, OUTPUT);
    }

    void checkTimer()
    {
      if (millis() - previousTime >= interval)
      {
        previousTime = millis();

        digitalWrite(pinNumber, !digitalRead(pinNumber));
      }
    }

}; //END of FlashLED

//********************************************^************************************************
//Make LED TIMERS
FlashLED pin13LEDtimer =
{
  1000ul,
  13
};

FlashLED pin12LEDtimer =
{
  100ul,
  12
};


//********************************************^************************************************
void setup()
{

}

//********************************************^************************************************
void loop()
{
  pin13LEDtimer.checkTimer();
  pin12LEDtimer.checkTimer();
}

Here is a struct to do this:


//********************************************^************************************************
struct FlashLED
{
  private:
    unsigned long previousTime;

  public:
    unsigned long interval;
    byte pinNumber;

    FlashLED(unsigned long interval_, byte pinNumber_)
    {
      interval = interval_;
      pinNumber = pinNumber_;

      pinMode (pinNumber, OUTPUT);
    }

    void checkTimer()
    {
      if (millis() - previousTime >= interval)
      {
        previousTime = millis();

        digitalWrite(pinNumber, !digitalRead(pinNumber));
      }
    }

}; //END of FlashLED

//********************************************^************************************************
//Make LED TIMERS
FlashLED pin13LEDtimer =
{
  1000ul,
  13
};

FlashLED pin12LEDtimer =
{
  100ul,
  12
};


//********************************************^************************************************
void setup()
{

}

//********************************************^************************************************
void loop()
{
  pin13LEDtimer.checkTimer();
  pin12LEDtimer.checkTimer();
}

The only difference is the keyword, class vs struct :face_with_monocle:



Edit

BTW The heartbeat LED in the Skeleton example toggles every 1/2 second.


//****************************************
MakeTimer heartbeatTIMER =
{
  500ul,    //.interval    (an unsigned long value)
  MILLIS,   //.timerType   (MILLIS or MICROS)
  YES,      //.restart     (YES or NO)
  ENABLED,  //.enabledFlag (ENABLED or DISABLED)
  YES       //.suspendable (YES or NO)
};

. . .


  //****************************************                      h e a r t b e a t T I M E R
  //is it time to toggle the heartbeatLED ?
  if (heartbeatTIMER.checkTimer() == EXPIRED)
  {
    //toggle the heartbeatLED
    digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
  }

1 Like

You have put the defining codes of the constructor function and other methods inside the class body -- could they be placed outside the class body and be defined below the loop() function?

class FlashLED
{
  private:
    int pinNumber;

  public:
    FlashLED(int DPin);
    void ioDir();
    void ledOn();
    void ledOff();
};

FlashLED pin13LED(13); 

void setup()
{
  pin13LED.ioDir();   
}

void loop()
{
  pin13LED.ledOn();
  delay(1000);
  pin13LED.ledOff();
  delay(1000);
}

void FlashLED::ioDir()
{
  pinMode(pinNumber, OUTPUT);
}

void FlashLED::ledOn()
{
  digitalWrite(pinNumber, HIGH);
}

void FlashLED::ledOff()
{
  digitalWrite(pinNumber, LOW);
}

FlashLED::FlashLED(int x)
{
  pinNumber = x;
}