Using classes in tabs in the Arduino IDE?

I didn’t actually realise tabs were a thing until I was looking at someone’s open source project and they used them for task specific groups of functions.

Anyway, I was trying it out for myself by putting a class in a tab and instantiating objects from the base sketch prior to setup(). I get errors that say ‘MyClassName’ does not name a type when Verifying the sketch. The class works fine when part of the base sketch - I just moved it into a tab.

Do tabs only work for functions? I looked for a guide to using tabs but couldn’t find one. I’d be happy to read for myself if one exists.

What file type have you put your class definitions into ?

It would help if you posted a small but complete example of the problem

1 Like

If you just want to split up a big sketch, you can split it all into tabs with the .ino extension. The IDE will paste them all together into one big .ino file before compiling and it will compile just as if they were in one file.

If you have an object class ‘X’ that you might eventually turn into a library (for use in multiple sketches), make an X.h tab for the declaration and an X.cpp tab for the functions too big to easily put in the class declaration. Then #include “X.h” in your main sketch. The X.h file will allow you to use the X class in your sketch. The X.cpp file will be compiled separately from you sketch and the functions you call will be added to your compiled sketch. Later, move the files to libraries/X/X.h and libraries/X/X.cpp and the #include “X.h” should still work.

1 Like

+1 for @ johnwasser’s suggestion.

In fact, you can break code into .h / .cpp files for enhanced modularity even if you don’t intend any of it to be reused as a library. See My Post #5 Here.

1 Like

Thank you very much, folks. Renaming the file and #include-ing it were the secret sauce I needed. For the sake of others that may search for something similar, I have made some examples that others may learn from. I have used Bill Earl's Flasher class along with Examples -> 02. Digital -> Button for the demo.

Here is the basic, monolithic version:

/*
   Classes demo 

   Change flash rate by pressing a button

*/

/*
   Flash Without Delay - Class

   From: https://learn.adafruit.com/multi-tasking-the-arduino-part-1/a-classy-solution
*/

class Flasher
{
    // We start by declaring a “Flasher” class:

    // Then we add in all the variables from FlashWithoutDelay.  Since they are part of
    // the class, they are known as member variables.


    // Class Member Variables
    // These are initialized at startup
    int ledPin;      // the number of the LED pin
    long OnTime;     // milliseconds of on-time
    long OffTime;    // milliseconds of off-time

    // These maintain the current state
    int ledState;                 // ledState used to set the LED
    unsigned long previousMillis;   // will store last time LED was updated


    // Next we add a constructor.  The constructor has the same name as the class
    // and its job is to initialize all the variables.
    // Constructor - creates a Flasher
    // and initializes the member variables and state
  public:
    Flasher(int pin, long on, long off)
    {
      ledPin = pin;

      OnTime = on;
      OffTime = off;

      ledState = LOW;
      previousMillis = 0;
    }

    void begin()
    {
      // Set the pinmode in setup() to give the hardware a chance to settle on startup
      pinMode(ledPin, OUTPUT);
    }
    
    // Finally we take our loop and turn it into a member function called “Update()”.
    // Note that this is identical to our original void loop().  Only the name has changed.
    void Update()
    {
      // check to see if it's time to change the state of the LED
      unsigned long currentMillis = millis();

      if ((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
      {
        ledState = LOW;  // Turn it off
        previousMillis = currentMillis;  // Remember the time
        digitalWrite(ledPin, ledState);  // Update the actual LED
      }
      else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
      {
        ledState = HIGH;  // turn it on
        previousMillis = currentMillis;   // Remember the time
        digitalWrite(ledPin, ledState);   // Update the actual LED
      }
    }

    // Now, for every LED that we want to flash, we create an instance of the Flasher class
    // by calling the constructor.  And on every pass through the loop we just need to call
    // Update() for each instance of Flasher.

    // Examples:
    //Flasher led1(12, 100, 400); // pin number, on time, off time
    //Flasher led2(13, 350, 350); // pin number, on time, off time

    // Then, in setup(), call the begin() method to set the pinmode.
    // void setup() {
    //   led1.begin();
    //   led2.begin();
    // }

}; // end of Flasher class

// Instantiate two LEDs
Flasher led1(LED_BUILTIN, 100, 600);
Flasher led2(LED_BUILTIN, 350, 350);

/* Configure the button */
const int buttonPin = 4;  // D4.
int buttonState = 0; // the pushbutton status

void setup() {
  // Sets the LED's pinMode
  led1.begin();
  led2.begin();
  
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed. If it is, the buttonState is LOW:
  if (buttonState == LOW) {
    led1.Update();
  } else if (buttonState == HIGH) {
    led2.Update();
  }
}

And here are the two three files for the tabbed version: base_sketch_name.ino, flasher.h, and flasher.cpp

/*
   Tabs with class demo 

   Change flash rate by pressing a button

   This is the base sketch
*/

#include "flasher.h"

// Instantiate two LEDs
Flasher led1(LED_BUILTIN, 100, 600);
Flasher led2(LED_BUILTIN, 350, 350);

/* Configure the button */
const int buttonPin = 4;  // D4.
int buttonState = 0; // the pushbutton status

void setup() {
  led1.begin();
  led2.begin();

  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed. If it is, the buttonState is LOW:
  if (buttonState == LOW) {
    led1.Update();
  } else if (buttonState == HIGH) {
    led2.Update();
  }
}

And the class broken into its own tabs/files, flasher.h and flasher.cpp:

/*
   Flash Without Delay - Class

   From: https://learn.adafruit.com/multi-tasking-the-arduino-part-1/a-classy-solution

   This is flasher.h
*/

#ifndef FLASHER_H
#define FLASHER_H

class Flasher
{
  private:
    // Class Member Variables
    int ledPin;      // the number of the LED pin
    long OnTime;     // milliseconds of on-time
    long OffTime;    // milliseconds of off-time

    // These maintain the current state
    int ledState;                 // ledState used to set the LED
    unsigned long previousMillis;   // will store last time LED was updated

  public:
    // Constructor
    Flasher(int pin, long on, long off);

    // Member functions
    void begin();
    void Update();
};

#endif

and flasher.cpp

/*
   Flash Without Delay - Class

   From: https://learn.adafruit.com/multi-tasking-the-arduino-part-1/a-classy-solution

   This is flasher.cpp
*/
#include "flasher.h"
#include "Arduino.h"

// Constructor
Flasher::Flasher(int pin, long on, long off)
    {
      ledPin = pin;

      OnTime = on;
      OffTime = off;

      ledState = LOW;
      previousMillis = 0;
    }

// Member functions
void Flasher::begin()
{
  // Set the pinmode in setup() to give the hardware a chance to settle on startup
  pinMode(ledPin, OUTPUT);
}

void Flasher::Update()
{
  // check to see if it's time to change the state of the LED
  unsigned long currentMillis = millis();

  if ((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
  {
    ledState = LOW;  // Turn it off
    previousMillis = currentMillis;  // Remember the time
    digitalWrite(ledPin, ledState);  // Update the actual LED
  }
  else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
  {
    ledState = HIGH;  // turn it on
    previousMillis = currentMillis;   // Remember the time
    digitalWrite(ledPin, ledState);   // Update the actual LED
  }
}

No .cpp file as suggested ?

1 Like

Thanks for the prod, @UKHeliBob . As I alluded to in my first post I have only really used one big .ino file with Arduino. Given this is my first time at splitting a class up this way, I hope my updated example is much better.

It seems to work OK and if it does what you want then great

One improvement would be not to set the pinMode() of the LED in the constructor as this runs very early in the sketch and may occur before the hardware is actually ready.

It would be better to have a begin method in the class that did that and call it from setup() by which time the hardware will be initialised

Thanks again. Have added your suggestion. Compiles and works on UNO R3, Seeed XIAO, and ESP32 DEVKIT V1 DOIT.