Have multiple classes in one script

Hello All,
How would I put 2 classes into 1 script on an Arduino UNO. Below code are two classes I would like to put in 1 script and have them work together. Please advise

Thanks

//=========================================================================
//first class
//=========================================================================
#define PIN_LED1 6
#define PIN_LED2 7
#define PIN_LED3 10
#define PIN_LED4 11

/*
   BLINKER CLASS DEFINITION
*/
class Blinker {
  private:
    byte pinLED;

    boolean ledState = LOW;

    unsigned long timeLedOn;
    unsigned long timeLedOff;

    unsigned long nextChangeTime = 0;

  public:
    Blinker(byte pinLED, unsigned long timeLedOn, unsigned long timeLedOff) {
      this->pinLED = pinLED;
      this->timeLedOn = timeLedOn;
      this->timeLedOff = timeLedOff;

      pinMode(pinLED, OUTPUT);
    }

    // Checks whether it is time to turn on or off the LED.
    void check() {
      unsigned long currentTime = millis();

      if (currentTime >= nextChangeTime) {

        if (ledState) {
          // LED is currently turned On. Turn Off LED.
          ledState = LOW;
          nextChangeTime = currentTime + timeLedOff;
        }
        else {
          // LED is currently turned Off. Turn On LED.
          ledState = HIGH;
          nextChangeTime = currentTime + timeLedOn;
        }

        digitalWrite(pinLED, ledState);
      }
    }
};

/*
    BLINKER CLASS VARIABLES DECLARATION
*/
Blinker blink1 = Blinker(PIN_LED1, 500, 500);
Blinker blink2 = Blinker(PIN_LED2, 1000, 1000);
Blinker blink3 = Blinker(PIN_LED3, 2000, 2000);
Blinker blink4 = Blinker(PIN_LED4, 1000, 2000);

void setup() {

}

void loop() {
  blink1.check();
  blink2.check();
  blink3.check();
  blink4.check();
}`Preformatted text`

//=========================================================================
//second class
//=========================================================================
/*
    Button’s. An Object Oriented approach  https://forum.arduino.cc/t/buttons-an-object-oriented-approach/279724/7
    An object oriented approach to a latched button
    Jan 2015, for the Arduino forum, by MattS-UK

    Compiled and tested on a Mega 2560 R3

    Do with it what you will.
*/

/*
  Hardware wiring
  Required:
   2 x momentary tac switch, normally open
   2 x Led
   2 x current limiting resistor ~220 Ohm to ~1K Ohm
   1 x breadboard

  pin2 ->--/ ------->gnd
  pin3 ->--/ ------->gnd
  pin4 ->--/\/\/---->gnd
  pin5 ->--/\/\/---->gnd
*/


//Button classs, encapsulate the functionality of a latched button
class Button {
  protected:
    uint8_t _pin;                  //hardware pin number
    boolean _state;                //current pin state
    boolean _wasPressed;           //button latch
    uint16_t _startDebounce;       //start of debounce time

  public:
    uint16_t debounceMs;            //period before button latches

    Button(uint8_t pin);
    void poll(uint16_t now);                            //call periodically to refresh the button state
    boolean wasPressed(void) {
      return _wasPressed;  //return the latch state
    }
    void reset(void);                                   //reset the latch
};

//constructor
Button::Button(uint8_t pin) {
  _pin = pin;
  _state = false;
  _startDebounce = false;
  _wasPressed = false;

  debounceMs = 500;
  pinMode(_pin, INPUT_PULLUP);
}

//refresh the button state
void Button::poll(uint16_t now) {

  //pullup resistors cause the
  // button to be HIGH when open
  // so we invert the hardware state
  _state = !digitalRead(_pin);

  //when the button state is false, reset the debounce time
  if (!_state) {
    _startDebounce = 0;
  }
  else {
    //start debounce time
    if (!_startDebounce) {
      _startDebounce = now;
    }
    else {
      //latch the button if it is still pressed when the debounce time expires
      if (now - _startDebounce > debounceMs) {
        _wasPressed = true;
      }
    }
  }
}

//reset the button
void Button::reset(void) {
  _wasPressed = false;
  _startDebounce = 0;
}


const uint8_t pinButton1 = 5;
const uint8_t pinButton2 = 8;
const uint8_t pinLed1 = 7;
const uint8_t pinLed2 = 6;

Button button1(pinButton1);
Button button2(pinButton2);

void setup(void) {

  pinMode(pinLed1, OUTPUT);
  pinMode(pinLed2, OUTPUT);

  button1.debounceMs = 100;  //set the button1 latch to 1 second
  button2.debounceMs = 100;  //set the button2 latch to 2 seconds
  //
  //button1.debounceMs = 1000;  //set the button1 latch to 1 second
  //button2.debounceMs = 2000;  //set the button2 latch to 2 seconds

}

uint16_t resetTime1 = 5000;    //led1 will latch for 5 seconds
uint16_t resetTime2 = 10000;   //led2 will latch for 10 seconds
//
//uint16_t resetTime1 = 5000;    //led1 will latch for 5 seconds
//uint16_t resetTime2 = 10000;   //led2 will latch for 10 seconds


uint16_t startResetTime1 = 0;  //we need a counter for led1
uint16_t startResetTime2 = 0;  //we need a counter for led2

void loop(void) {
  uint16_t now = millis();
  button1.poll(now);        //refresh button1
  button2.poll(now);        //refresh button2


  if (button1.wasPressed()) {
    digitalWrite(pinLed1, HIGH);
    if (!startResetTime1) startResetTime1 = now;
    if (now - startResetTime1 > resetTime1) {
      startResetTime1 = 0;
      digitalWrite(pinLed1, LOW);
      button1.reset();
    }
  }

  if (button2.wasPressed()) {
    digitalWrite(pinLed2, HIGH);
    if (!startResetTime2) startResetTime2 = now;
    if (now - startResetTime2 > resetTime2) {
      startResetTime2 = 0;
      digitalWrite(pinLed2, LOW);
      button2.reset();
    }
  }
}




What is your question?

Hello gfvalvo,
How do I put these 2 classes in 1 script to do a blink and control the button presses latch time. One class controls the latch time on a button press, the other class blinks the leds, I want to combine them into one script,

You cut & paste them into the same sketch. Since every sketch can only contain one setup() and one loop() function, you have to combine the contents of both of those into the same function. Those are not really part of the class. Something like this

// add the class code for Blinker
// add the class code for Button
void setup() {
  // contents from blinker
 // contents from button
}

void loop() {
  // contents from blinker
 // contents from button
}

I'll see if I can get it to work. Thanks

keeps crashing
_2021_my_learning_arduino_oop_1:116:1: error: 'Blinker' does not name a type
Arduino: 1.8.16 (Windows Store 1.8.51.0) (Windows 10), Board: "Arduino Uno"

/*
    Button’s. An Object Oriented approach  https://forum.arduino.cc/t/buttons-an-object-oriented-approach/279724/7
    An object oriented approach to a latched button
    Jan 2015, for the Arduino forum, by MattS-UK

    Compiled and tested on a Mega 2560 R3

    Do with it what you will.
*/

/*
  Hardware wiring
  Required:
   2 x momentary tac switch, normally open
   2 x Led
   2 x current limiting resistor ~220 Ohm to ~1K Ohm
   1 x breadboard

  pin2 ->--/ ------->gnd
  pin3 ->--/ ------->gnd
  pin4 ->--/\/\/---->gnd
  pin5 ->--/\/\/---->gnd
*/
#define PIN_LED1 6
#define PIN_LED2 7
#define PIN_LED3 10
#define PIN_LED4 11

boolean ledState = LOW;

    unsigned long timeLedOn;
    unsigned long timeLedOff;

    unsigned long nextChangeTime = 0;





//Button classs, encapsulate the functionality of a latched button
class Button {
  protected:
    uint8_t _pin;                  //hardware pin number
    boolean _state;                //current pin state
    boolean _wasPressed;           //button latch
    uint16_t _startDebounce;       //start of debounce time

  public:
    uint16_t debounceMs;            //period before button latches

    Button(uint8_t pin);
    void poll(uint16_t now);                            //call periodically to refresh the button state
    boolean wasPressed(void) {
      return _wasPressed;  //return the latch state
    }
    void reset(void);                                   //reset the latch
};

//constructor
Button::Button(uint8_t pin) {
  _pin = pin;
  _state = false;
  _startDebounce = false;
  _wasPressed = false;

  debounceMs = 500;
  pinMode(_pin, INPUT_PULLUP);
}

//refresh the button state
void Button::poll(uint16_t now) {

  //pullup resistors cause the
  // button to be HIGH when open
  // so we invert the hardware state
  _state = !digitalRead(_pin);

  //when the button state is false, reset the debounce time
  if (!_state) {
    _startDebounce = 0;
  }
  else {
    //start debounce time
    if (!_startDebounce) {
      _startDebounce = now;
    }
    else {
      //latch the button if it is still pressed when the debounce time expires
      if (now - _startDebounce > debounceMs) {
        _wasPressed = true;
      }
    }
  }
}

//reset the button
void Button::reset(void) {
  _wasPressed = false;
  _startDebounce = 0;
}


const uint8_t pinButton1 = 5;
const uint8_t pinButton2 = 8;

const uint8_t pinLed1 = 7;
const uint8_t pinLed2 = 6;
const uint8_t pinLed3 = 10;
const uint8_t pinLed4 = 11;

Button button1(pinButton1);
Button button2(pinButton2);
/*
    BLINKER CLASS VARIABLES DECLARATION
*/
Blinker blink1 = Blinker(pinLed1, 500, 500);
Blinker blink2 = Blinker(pinLed2, 1000, 1000);
Blinker blink3 = Blinker(pinLed3, 2000, 2000);
Blinker blink4 = Blinker(pinLed4, 1000, 2000);
void setup(void) {

  pinMode(pinLed1, OUTPUT);
  pinMode(pinLed2, OUTPUT);

  button1.debounceMs = 100;  //set the button1 latch to 1 second
  button2.debounceMs = 100;  //set the button2 latch to 2 seconds
  //
  //button1.debounceMs = 1000;  //set the button1 latch to 1 second
  //button2.debounceMs = 2000;  //set the button2 latch to 2 seconds

}

uint16_t resetTime1 = 5000;    //led1 will latch for 5 seconds
uint16_t resetTime2 = 10000;   //led2 will latch for 10 seconds
//
//uint16_t resetTime1 = 5000;    //led1 will latch for 5 seconds
//uint16_t resetTime2 = 10000;   //led2 will latch for 10 seconds


uint16_t startResetTime1 = 0;  //we need a counter for led1
uint16_t startResetTime2 = 0;  //we need a counter for led2

void loop(void) {
  uint16_t now = millis();
  button1.poll(now);        //refresh button1
  button2.poll(now);        //refresh button2
  blink1.check();
  blink2.check();
  blink3.check();
  blink4.check();

  if (button1.wasPressed()) {
    digitalWrite(pinLed1, HIGH);
    if (!startResetTime1) startResetTime1 = now;
    if (now - startResetTime1 > resetTime1) {
      startResetTime1 = 0;
      digitalWrite(pinLed1, LOW);
      button1.reset();
    }
  }

  if (button2.wasPressed()) {
    digitalWrite(pinLed2, HIGH);
    if (!startResetTime2) startResetTime2 = now;
    if (now - startResetTime2 > resetTime2) {
      startResetTime2 = 0;
      digitalWrite(pinLed2, LOW);
      button2.reset();
    }
  }
}

// Checks whether it is time to turn on or off the LED.
    void check() {
      unsigned long currentTime = millis();

      if (currentTime >= nextChangeTime) {

        if (ledState) {
          // LED is currently turned On. Turn Off LED.
          ledState = LOW;
          nextChangeTime = currentTime + timeLedOff;
        }
        else {
          // LED is currently turned Off. Turn On LED.
          ledState = HIGH;
          nextChangeTime = currentTime + timeLedOn;
        }

        digitalWrite(pinLed1, ledState);
      }
    }
//};

Everyone has an opinion on how to do this...

As or myself, I create one .h and one.cpp file per class. If you put 'em in the same folder as the sketch, 4 tabs will be populated. Or alternately stick them in the library folder following directory naming protocol and just #include the .h 's as with any other library.

If in the same folder the main sketch will generally look like:

#include "./class1.h"
#include "./class2.h"

and in each of the class1.cpp and class2.cpp you will #include the appropriate .h file.

You have omitted the definion of the class Blinker from your sketch hence it goes wrong here:

Blinker blink1 = Blinker(pinLed1, 500, 500);

It may also not be your problem here but you should not call Arduino functions from the contructor because the initialisation of the Arduino environment is not complete at the time the construtors are invoked and could lead to unpredictable results. In this case you are calling pinMode().

//constructor
Blinker(byte pinLED, unsigned long timeLedOn, unsigned long timeLedOff) {
      this->pinLED = pinLED;
      this->timeLedOn = timeLedOn;
      this->timeLedOff = timeLedOff;

      pinMode(pinLED, OUTPUT);
}

//constructor
Button::Button(uint8_t pin) {
  _pin = pin;
  _state = false;
  _startDebounce = false;
  _wasPressed = false;

  debounceMs = 500;
  pinMode(_pin, INPUT_PULLUP);
}

Could you show me? I am new to classes and libraries

That is not a crash. It is a failure for your code to compile. Crashing is when your code compiles and uploads to the board but then does unexpected, random things including resets....

Right

Surely.

See attached for ESP32 and the main file is graphictest.ino

Note how the libraries have been made local to the sketch.

graphicstest.zip (60.6 KB)

didn't compile no such file or directory
#include "./Adafruit_GFX.h"

You asked for an example; it's ESP32 code and requires the Espressif compiler installation, etc. I have not attempted to recompile in a couple of years, so minor adjustments may be needed. But, if once compiled as shown in the main file header...

/************************************************
  Sketch uses 278809 bytes (21%) of program storage space. Maximum is 1310720 bytes.
  Global variables use 15660 bytes (4%) of dynamic memory, leaving 312020 bytes for local variables. Maximum is 327680 bytes.
  Arduino 1.8.12 on Windows 10 Pro
  Note: Works at 80MHz and 160MHz
  python /home/ray/.arduino15/packages/esp32/tools/esptool_py/2.6.1/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect 0xe000 /home/ray/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/partitions/boot_app0.bin 0x1000 /home/ray/.arduino15/packages/esp32/hardware/esp32/1.0.4/tools/sdk/bin/bootloader_qio_80m.bin 0x10000 /tmp/arduino_build_528244/graphicstest.ino.bin 0x8000 /tmp/arduino_build_528244/graphicstest.ino.partitions.bin 
  esptool.py v2.6
  Serial port /dev/ttyUSB0
  Connecting........___
  Chip is ESP32D0WDQ6 (revision 0)
  Features: WiFi, BT, Dual Core, Coding Scheme None
  MAC: 24:0a:c4:05:78:6c
  Uploading stub...
  
 Modified graphics demo for the ESP-WROVER-KIT and equivalent 
 projects using Ardiono-ESP32 with an ILI9341 LCD display
 By Martin Falatic
 Implemented by MRB 20200527 on Win 10 PRo w/ IDE 1.8.12
 ****************************************************/

All code must be unZipped into a sketch folder named "graphicstest" ...
Open the graphicstest.ino in the IDE

Going to look like this in the IDE

The point is, you have TABS and can use them for notes, for .ino code, for .h header code, and for .cpp code normally used for class libraries. The "rules" are rather flexible.

will continue this another day I have to run. Thanks for your help

The fast and dirty way to merge two (or more) working sketches into one (possibly working) sketch is to create this main sketch:

void setup()
{
  setup1();
  setup2();
  // setup3();
  // setup4();
}
void loop()
{
  loop1();
  loop2();
  // loop3();
  // loop4{};
}

Then, put the two (or more) sketches into 'tabs'. You can add, remove, and rename tabs with the tab menu: the little triangle at the right end of the tab bar.

Rename the setup() and loop() in each tab to setup1()/loop1() in the first tab, setup2()/loop2() in the second, etc.

If you are lucky there will be no problems and both sketches will take turns so fast they will appear to be running 'at the same time'. If you are unlucky, you will have to resolve conflicts and rewrite each sketch that hogs the CPU.

Problems to look for:

Name Conflicts: Two or more sketches defining global variables or functions of the same name. You will have to change the names until there are no conflicts.

Pin conflicts: Two or more sketches using the same pin number for different purposes. You will have to change pin assignments to avoid conflicts.

Hardware Usage Conflicts: Two or more sketches that use the same hardware feature in different ways. For example, using the Serial port at different data rates. If you can get them all to agree on a data rate you can replace the several Serial.begin(rate) calls with one at the top of setup() in the 'main' sketch.

Library Conflicts: Some libraries don't work together because they use the same hardware for different purposes. For example, the Servo library uses Timer1 and so does the IRremote library. If you are lucky you will get an error message when they both try to use the same hardware interrupt. If you are not lucky, things just won't work.