How to use a library in my own custom library and also in my main program?

Hi all!

I am using an ESP32 with a touchscreen display using the GitHub - Bodmer/TFT_eSPI: Arduino and PlatformIO IDE compatible TFT library optimised for the Raspberry Pi Pico (RP2040), STM32, ESP8266 and ESP32 that supports different driver chips library. What I would like to do is create my own library where most of my graphics code would be and use the TFT_eSPI library. I also need to use the TFT_eSPI library from within my main program and I can’t figure out how to do that. Here’s what I have so far…

My main program:

// display
#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>

TFT_eSPI tft = TFT_eSPI();        // Invoke custom library

#include "Switch.h"

Switch sw1 = Switch(160,120, 64, 32, false);

//Switch sw2(80, 60, 64, 32, true);




void setup() {
  // put your setup code here, to run once:

  Serial.begin(115200);
  Serial.println(" starting... \n");
  
  tft.init();
  tft.setRotation(3);

  tft.fillScreen(TFT_BLACK);

  uint16_t calData[5] = { 449, 3418, 344, 3395, 1 };
  tft.setTouch(calData);
  
  sw1.drawSquareSwitch();

  //sw2.drawRoundSwitch();
  
  
}


void loop() {
  // put your main code here, to run repeatedly:

}

My Switch.h file:

// Switch.h

#ifndef Switch_h
#define Switch_h

#include "Arduino.h"


class Switch
{
  public:
    Switch(int x, int y, int w, int h, bool st);
    
    void drawSquareSwitch();

    int _xLoc;
    int _yLoc;
    int _xSize;
    int _ySize;
    bool _state;
};

#endif

and my Switch.cpp file:

// Switch.cpp

#include "Arduino.h"
#include "Switch.h"


#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>

TFT_eSPI tft = TFT_eSPI();        // Invoke custom library


Switch::Switch(int x, int y, int w, int h, bool st)
{
  int _xLoc = x;
  int _yLoc = y;
  int _xSize = w;
  int _ySize = h;
  bool _state = st;
  
}


void Switch::drawSquareSwitch()
{
  // draw a square, red/green switch graphic
  
 
  tft.drawRect((_xLoc - (_xSize/2)), (_yLoc - (_ySize/2)), _xSize, _ySize, TFT_WHITE);
  tft.drawFastVLine((_xLoc), (_yLoc - (_ySize/2)), _ySize, TFT_WHITE);

  // color state switch - which part of the switch is on 
  if (_state == true)
  {
    tft.fillRect((_xLoc - ((_xSize / 2) - 1)), (_yLoc - ((_ySize / 2) - 1)), ((_xSize / 2) - 1), (_ySize - 2), TFT_GREEN);    // fill 'on' side of switch green in color  
    tft.fillRect((_xLoc + 1), (_yLoc - ((_ySize / 2) - 1)), ((_xSize / 2) - 2), (_ySize - 2), TFT_BLACK);                    // fill 'off' side of switch black in color
  } 
  else
  {
    tft.fillRect((_xLoc - ((_xSize / 2) - 1)), (_yLoc - ((_ySize / 2) - 1)), ((_xSize / 2) - 1), (_ySize - 2), TFT_BLACK);    // fill 'on' side of switch black in color
    tft.fillRect((_xLoc + 1), (_yLoc - ((_ySize / 2) - 1)), ((_xSize / 2) - 2), (_ySize - 2), TFT_RED);                      // fill 'off' side of switch red in color
  }
 
} // end - void drawSwitch()

When compiled, I get the error message:

sketch\Switch_class.ino.cpp.o:(.bss.tft+0x0): multiple definition of `tft’
sketch\Switch.cpp.o:(.bss.tft+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board ESP32 Dev Module.

So how do I use the TFT_eSPI library in both my custom library and in my main program?

Thanks for any help!
Randy

remove
TFT_eSPI tft = TFT_eSPI();
from sketch

add
extern TFT_eSPI tft;
to switch.h

this will make the tft from Switch.cpp visible to sketch

Thanks @Juraj

Juraj:
remove
TFT_eSPI tft = TFT_eSPI();
from sketch

add
extern TFT_eSPI tft;
to switch.h

this will make the tft from Switch.cpp visible to sketch

But that didn’t quite work, it produced a ‘does not name a type’ error in switch.h when compiled. I did read up a bit on extern keyword a bit. What did work was replacing:

TFT_eSPI tft = TFT_eSPI();

from the sketch with:

extern TFT_eSPI tft;

Now tft can be used from both the sketch and the Switch.cpp file. Here’s my new sketch with the change:

// Switch_test

// display
#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>

//TFT_eSPI tft = TFT_eSPI();        // Invoke custom library
extern TFT_eSPI tft;

#include "Switch.h"

Switch sw1 = Switch(160,120, 64, 32, true);

//Switch sw2(80, 60, 64, 32, true);


void setup() {
  // put your setup code here, to run once:

  Serial.begin(115200);
  Serial.println(" starting... \n");
  
  tft.init();
  tft.setRotation(3);

  tft.fillScreen(TFT_BLACK);

  tft.setCursor(60, 60);
  tft.print("hello world");
  delay(1000);

  uint16_t calData[5] = { 449, 3418, 344, 3395, 1 };
  tft.setTouch(calData);
  
  sw1.drawSquareSwitch();

  //sw2.drawRoundSwitch();
   
}


void loop() {
  // put your main code here, to run repeatedly:

  tft.fillCircle(200, 200, 4, TFT_GREEN);
  delay(1000);
  tft.fillCircle(200, 200, 4, TFT_BLACK);
  delay(1000);
  

}

There is some extra code added in to troubleshoot another problem, but that’s not related to this problem.

So thanks for the push in the right direction and now I know I should study up on the ‘extern’ keyword a bit more.

Randy

It's often better to extend the class using inheritance, so that your function just becomes another method of the base class. It is easier to interface the rest of the base class that way.

Then you would find your new function available with all the rest, like:

tft.drawSquareSwitch();

instead of requiring two classes, e.g.

tft.yadda();
tft.yaddaYadda();
myCustomClass.drawSquareSwitch();

revolt_randy:
Thanks @Juraj

But that didn’t quite work, it produced a ‘does not name a type’ error in switch.h when compiled. I did read up a bit on extern keyword a bit. What did work was replacing:

TFT_eSPI tft = TFT_eSPI();

from the sketch with:

extern TFT_eSPI tft;

Now tft can be used from both the sketch and the Switch.cpp file. Here’s my new sketch with the change:

// Switch_test

// display
#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>

//TFT_eSPI tft = TFT_eSPI();        // Invoke custom library
extern TFT_eSPI tft;

#include “Switch.h”

Switch sw1 = Switch(160,120, 64, 32, true);

//Switch sw2(80, 60, 64, 32, true);

void setup() {
  // put your setup code here, to run once:

Serial.begin(115200);
  Serial.println(" starting… \n");
 
  tft.init();
  tft.setRotation(3);

tft.fillScreen(TFT_BLACK);

tft.setCursor(60, 60);
  tft.print(“hello world”);
  delay(1000);

uint16_t calData[5] = { 449, 3418, 344, 3395, 1 };
  tft.setTouch(calData);
 
  sw1.drawSquareSwitch();

//sw2.drawRoundSwitch();
 
}

void loop() {
  // put your main code here, to run repeatedly:

tft.fillCircle(200, 200, 4, TFT_GREEN);
  delay(1000);
  tft.fillCircle(200, 200, 4, TFT_BLACK);
  delay(1000);

}




There is some extra code added in to troubleshoot another problem, but that's not related to this problem.

So thanks for the push in the right direction and now I know I should study up on the 'extern' keyword a bit more.

Randy

in switch.spp you should include the tft library before switch.h or better put the #include <TFT_eSPI.h> in switch.h

Thanks for the replies!

Juraj:
in switch.spp you should include the tft library before switch.h or better put the #include <TFT_eSPI.h> in switch.h

@Juraj - you are correct, editing the Switch.cpp file as suggested works, with the ‘extern TFT_eSPI tft;’ in the Switch.h file. For some reason, I think the #include <TFT_eSPI.h> in Switch.h would be better. But hey, what do I know? I’m a bit lost in this…

aarg:
It’s often better to extend the class using inheritance, so that your function just becomes another method of the base class. It is easier to interface the rest of the base class that way.

I did a quick search and read on class inheritance and I think I basically got the general idea. Why would this be a better way? Is there any gains to using this method (other than furthering my knowledge)? If this is the common way to handle something like this, then tell me that’s how everyone does it. Industry common practices exist for a reason, they work…

Randy