Libraries concepts and implementation doubts

Please, I’d like to ask your help.

I’ve been avoiding the use of home made “.h” libraries for a long time because of my own missunderstanding about concepts like depencencies. Nevertheless I’m aware spliting the code among my own libraries can make the program much easier for reading and debugging.

Now I’m trying produce new libraries that might be reused in further projects, particularly in those with the same pieces of hardware.

Moreover, since some libraries are shared by several projects (TFT_eSPI, for example), commenting and uncommenting pieces of definitions within their codes is very work intensive and causes troubles to projects that use distinct definitions.

In the present project, an ESP32 will manage a TFT display, touch panel, serial communications and hardware pin IOs. So far, I’m focussing on the setup of ESP32 and the TFT display.

Then, the program comprehends now:

  • main.c;
  • Hardware.h/.cpp for declaring and defining properties of any hardware interfaces present in the project;
  • H24-1015_TFT.h/.cpp for holding all content display funcions related to this application;
  • TFT_eSPI.h/.c is the one distributed by github;
  • TFT_eSPI_ESP32.h/.c, User_Setup.h, User_Setup_Select.h are headers called by TFT_eSPI.h.

The configuraton and dependencies are:

Processing esp32dev (platform: espressif32; board: esp32dev; framework: arduino)
---------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32dev.html
PLATFORM: Espressif 32 (6.5.0) > Espressif ESP32 Dev Module
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
DEBUG: Current (cmsis-dap) External (cmsis-dap, esp-bridge, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES: 
 - framework-arduinoespressif32 @ 3.20014.231204 (2.0.14) 
 - tool-esptoolpy @ 1.40501.0 (4.5.1) 
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 38 compatible libraries
Scanning dependencies...
Dependency Graph
|-- TFT_eSPI @ 2.5.43
|-- Adafruit GFX Library @ 1.11.9
|-- Adafruit BusIO @ 1.15.0
|-- H24-1015_TFT
|-- Hardware
|-- SPI @ 2.0.0
Building in release mode
Compiling .pio/build/esp32dev/src/main.cpp.o
Compiling .pio/build/esp32dev/lib619/TFT_eSPI/TFT_eSPI.cpp.o
Archiving .pio/build/esp32dev/lib5c7/libAdafruit BusIO.a
Compiling .pio/build/esp32dev/libf19/Adafruit GFX Library/Adafruit_GrayOLED.cpp.o
Indexing .pio/build/esp32dev/lib5c7/libAdafruit BusIO.a
Compiling .pio/build/esp32dev/libf19/Adafruit GFX Library/Adafruit_SPITFT.cpp.o

Let me give you some pieces of code:

From main.c:

#include "Hardware.h"
#include <SPI.h>
#include "H24-1015_TFT.h"

void setup()
{
  tft.init();
  tft.setRotation(1);
  tft.fillScreen(TFT_BLACK);
  tft.setCursor(0, 0, 4);
  tft.setTextColor(TFT_WHITE);
  tft.println("Hello World!");
}

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

From Hardware.h:

#if !defined(_Hardware_H)
#define _Hardware_H

//  Config SPI (VSPI) TFT
#define TFT_DC      
#define TFT_SCLK    
#define TFT_MOSI    
#define TFT_MISO     //  Manter desconectado caso o MISO seja compartilhado com outro dispositivo
#define TFT_CS          //  Pino de seleção do TFT no barramento SPI
#define TFT_RST       //  Pino de RESET

// Config Display
extern int TFT_largura,
            TFT_altura;

//  Config SPI (VSPI) TOUCH
#define TOUCH_DC    
#define TOUCH_SCLK  
#define TOUCH_MOSI  
#define TOUCH_MISO  
#define TOUCH_CS     //  Pino de seleção do TOUCH no barramento SPI

#define SPI_FREQUENCY           //  Da biblioteca Setup21_ILI9488.h
#define SPI_READ_FREQUENCY      //  
#define SPI_TOUCH_FREQUENCY      //  

#endif

From Hardware.cpp:

//  Config SPI (VSPI) TFT
#define TFT_DC      33
#define TFT_SCLK    18
#define TFT_MOSI    23
#define TFT_MISO    19 //  Manter desconectado caso o MISO seja compartilhado com outro dispositivo
#define TFT_CS       5    //  Pino de seleção do TFT no barramento SPI
#define TFT_RST     -1  //  Pino de RESET

// Config Display
int TFT_largura = 320,
    TFT_altura = 480;

//  Config SPI (VSPI) TOUCH
#define TOUCH_DC    33
#define TOUCH_SCLK  18
#define TOUCH_MOSI  23
#define TOUCH_MISO  19
#define TOUCH_CS    32 //  Pino de seleção do TOUCH no barramento SPI

#define SPI_FREQUENCY 27000000          //  Da biblioteca Setup21_ILI9488.h
#define SPI_READ_FREQUENCY 16000000     //  
#define SPI_TOUCH_FREQUENCY 2500000     //

From H24-1015_TFT.h:

// Config for User_Setup.h
#define TFT_RGB_ORDER TFT_RGB            // Colour order Red-Green-Blue
#define ILI9488_DRIVER                   // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)

#pragma once
    #include "Hardware.h"
    #include "TFT_eSPI.h"
#include "Adafruit_GFX.h"
// #include "Adafruit_GrayOLED.h"
#include "Adafruit_SPITFT.h"
#include "Adafruit_SPITFT_Macros.h"
#include "gfxfont.h"

#include "User_Setup.h"                  // Default setup is root library folder
#include "User_Setups/Setup21_ILI9488.h" // Configurações do arquivo Setup21_ILI9488.h
                                         //     > TFT ILI9488 em bus SPI para ESP32

// "tft" as a TFT_eSPI instance
TFT_eSPI tft;

From H24-1015_TFT.cpp:

#include "H24-1015_TFT.h"

// TFT_eSPI tft = TFT_eSPI(TFT_lartura, TFT_altura);
TFT_eSPI tft = TFT_eSPI(TFT_largura, TFT_altura);

When compiling, dozens of warning and error messages are listed. Here you are some exemples:

In file included from .pio/libdeps/esp32dev/TFT_eSPI/TFT_eSPI.h:104,
                 from lib/H24-1015_TFT/H24-1015_TFT.h:30,
                 from src/main.cpp:95:
.pio/libdeps/esp32dev/TFT_eSPI/Processors/TFT_eSPI_ESP32.h:164:17: error: operator '>=' has no left operand
     #if (TFT_DC >= 32)
                 ^~
.pio/libdeps/esp32dev/TFT_eSPI/Processors/TFT_eSPI_ESP32.h:174:19: error: operator '>=' has no left operand
     #elif (TFT_DC >= 0)
In file included from .pio/libdeps/esp32dev/TFT_eSPI/TFT_eSPI.cpp:16:
.pio/libdeps/esp32dev/TFT_eSPI/TFT_eSPI.h:976:8: warning: #warning >>>>------>> TOUCH_CS pin not defined, TFT_eSPI touch functions will not be available! [-Wcpp]
       #warning >>>>------>> TOUCH_CS pin not defined, TFT_eSPI touch functions will not be available!
        ^~~~~~~
In file included from .pio/libdeps/esp32dev/TFT_eSPI/TFT_eSPI.cpp:16:
.pio/libdeps/esp32dev/TFT_eSPI/TFT_eSPI.h:435:25: error: 'TFT_WIDTH' was not declared in this scope
   TFT_eSPI(int16_t _W = TFT_WIDTH, int16_t _H = TFT_HEIGHT);
                         ^~~~~~~~~
.pio/libdeps/esp32dev/TFT_eSPI/TFT_eSPI.h:435:25: note: suggested alternative: 'TFT_WHITE'
   TFT_eSPI(int16_t _W = TFT_WIDTH, int16_t _H = TFT_HEIGHT);
                         ^~~~~~~~~
                         TFT_WHITE
.pio/libdeps/esp32dev/TFT_eSPI/TFT_eSPI.h:435:49: error: 'TFT_HEIGHT' was not declared in this scope
   TFT_eSPI(int16_t _W = TFT_WIDTH, int16_t _H = TFT_HEIGHT);
                                                 ^~~~~~~~~~

Your contribution will be very usefull for me.
Thak you all.
Best regards,
Ciro Bruno

Hi @ciro_bruno !

Your approach contains at least 2 errors:

  1. In your hardware.h you try to pre-declare macros like variables to define them later in the hardware.cpp. This is not possible. Unlike variables, macros cannot be predeclared.

  2. The TFT_eSPI is a very good library, but unfortunately cumbersome to configure (manual editing of the library files).

    For PlatformIO you can configure the TFT_eSPI library via the build_flags in the platformio.ini. Take a look at this article:

Alternatively, you can create a header file (e.g. tft_settings.h) with all the required macros and include this “globaly” by using build_flags = -include "tft_settings.h".

1 Like

Thank you.

Solved. I’ve placed all those declarations in Hardware.h.
Hardware.h:

#if !defined(_Hardware_H)

#define _Hardware_H
#define Hardware_VERSION "1.0.0"

#include <stdint.h>

//  Configuração do SPI (VSPI) para o TFT
#define TFT_DC 33
#define TFT_SCLK 18
#define TFT_MOSI 23
#define TFT_MISO 19 //  Manter desconectado caso o MISO seja compartilhado com outro dispositivo
#define TFT_CS 5    //  Pino de seleção do TFT no barramento SPI
#define TFT_RST -1  //  Pino de RESET

//  Configuração do SPI (VSPI) para o TOUCH
#define TOUCH_DC 33
#define TOUCH_SCLK 18
#define TOUCH_MOSI 23
#define TOUCH_MISO 19
#define TOUCH_CS 32 //  Pino de seleção do TOUCH no barramento SPI

#include "H24_1015_TFT.h"

#endif

I’ve also replaced the “-” char from H24-1015_TFT.h/.cpp by “_”, in order to avoid possible other problems. Now it’s called H24_1015_TFT.h/.cpp

That’s what I intend to avoid: editing the library manually. I don’t want to edit this library every time I have switch between projects that include it.

The solution through platformio.ini, via build_flags is very good. But this is a PlatformIO specific resource.

My concern is about the code portability. In the future I might have to review this code in some other environment which might not offer this resource. Then, I’d rather have all definitions embeded in the code itself.

Does it make sense?

Yes.

This tft_settings.h is my H24_1015_TFT.h. My intended structure is:

  • main.c includes Hardware.h, which is supposed to define all hardware interfaces, from pin assignment to the inclusion of other hardware specific peripherals’ macros/libraries;
  • Hardware.h/.cpp includes H24_1015_TFT.h, that would responsible for all TFT definitions and for including TFT_eSPI.h, making it possible to keep TFT_eSPI.h/.cpp untouched.

Let me show you the way I’m trying to do it:

In main.c:

(...)
#include "Hardware.h"
(...)

In Hardware.h:

#if !defined(_Hardware_H)

#define _Hardware_H
#define Hardware_VERSION "1.0.0"

#include <stdint.h>

//  Configuração do SPI (VSPI) para o TFT
#define TFT_DC 33
#define TFT_SCLK 18
#define TFT_MOSI 23
#define TFT_MISO 19 //  Manter desconectado caso o MISO seja compartilhado com outro dispositivo
#define TFT_CS 5    //  Pino de seleção do TFT no barramento SPI
#define TFT_RST -1  //  Pino de RESET
// #define TFT_RST  -1  //  Atribuir "-1" caso o RESET do TFT seja ligado ao RESET do ESP32

//  Configuração do SPI (VSPI) para o TOUCH
#define TOUCH_DC 33
#define TOUCH_SCLK 18
#define TOUCH_MOSI 23
#define TOUCH_MISO 19
#define TOUCH_CS 32 //  Pino de seleção do TOUCH no barramento SPI

#include "H24_1015_TFT.h"

#endif

In H24_1015_TFT.h:

#if !defined(_H24_1015_TFT_H)
#define _H24_1015_TFT_H

#if defined(_Hardware_H)
#include "Hardware.h"
#endif

#define H24_1015_TFT_VERSION "1.0.0"

// Configurações do arquivo User_Setup.h
#define TFT_RGB_ORDER TFT_RGB            // Colour order Red-Green-Blue
#define ILI9488_DRIVER                   // WARNING: Do not connect ILI9488 display SDO to MISO if other devices share the SPI bus (TFT SDO does NOT tristate when CS is high)
// Configurações do Display
#define TFT_WIDTH 320
#define TFT_HEIGHT 480

#define SPI_FREQUENCY 27000000      //  Da biblioteca Setup21_ILI9488.h
#define SPI_READ_FREQUENCY 16000000 //
#define SPI_TOUCH_FREQUENCY 2500000 //

#include <stdint.h>
#include "TFT_eSPI.h"
#include "User_Setup.h"                  // Default setup is root library folder
#include "User_Setups/Setup21_ILI9488.h" // Configurações do arquivo Setup21_ILI9488.h
                                         //     > TFT ILI9488 em bus SPI para ESP32

TFT_eSPI tft = TFT_eSPI();

#include "Adafruit_GFX.h"
// #include "Adafruit_GrayOLED.h"
#include "Adafruit_SPITFT.h"
#include "Adafruit_SPITFT_Macros.h"
#include "gfxfont.h"

#endif

So far, it’s not working. I can send you the failures report. But now I’m focussing on understanding this concept right. I guess you’ll find mistakes in it.

Thank you.
Best regards,
Ciro

This is a drawback of the library. PlatformIO offers you an elegant solution to compensate for this drawback.

I have a different point of view:
You are dealing with a library that is heavily dependent on the drawbacks of the ArduinoIDE. Again, this is a problem of the library! TFT_eSPI will not be very portable in this respect. Then you should rather look for a library that adheres to the C / C++ standards.
If the library is not future-proof, this is not the fault of PlatformIO.
And nobody can tell you today what solutions your future IDE will offer :wink:

1 Like

Why does hardware.h include H24_1015_TFT.h?

Why does H24_1015_TFT.h include Hardware.h?

This should go also in hardware.h
Then you can simply use build_flags = -include "hardware.h" and you’re good to go.

My view on this is:
Real libraries (header files ‘.h’ and source files ‘.cpp’) should declare and implement functions which are reusable.
A header file that only defines macros is nothing more than a configuration file.

By the way, this makes no sense to me.

The macro _HARDWARE_H is defined in hardware.h
So the statement will only become true if hardware.h is already included.

It’s supposed to contain functions.

The idea is to place in Hardware.cpp the functions for setting up and initializing all MCU internal devices, such as SPI, I2C and UART interfaces, RTC, WiFi and Bluetooth, as well as configuring ModBus RTU and TCP.

It doesn’t have to contain only that. But I guess this way It might make it easier to find the routines.

To make the pin assingments of Hardware.h visible in_H24_1015_TFT.h/.cpp_.

Is this concept correct?

Then there’s nothing wrong with that :slight_smile:
But leave out the configuration of the TFT_eSPI library.
This belongs in a configuration file (like a platformio.ini) - not in a library.

1 Like

No, you don’t need this (for the TFT_eSPI part).
You don’t need to call any TFT_eSPI function wich initializing a GPIO or something like that.
All this is done by the ugly way TFT_eSPI goes (by using macro definitions).

As far as I can see, your H24_1015_TFT.h is just a bunch of includes.
No functions are declared. No functions are implemented.

You’re right. There might be some small function for “cosmetics” to be displayed. But this wouldn’t realy justify to have this dependency.

I’ll probably move this to Hardware.cpp, since TFT_eSPI doesn’t require much “assistance”.

Btw, where should I #include TFT_eSPI? in Hardware.h?

No!

I think you still have a misunderstanding about headers and implementation files. This may come from the ArduinoIDE era where there are no real differences or things are handled globally and differently.

I would suggest you create a small and very simple project and practice using and creating header and implementation files.

(I also had huge problems and misunderstandings with this at the beginning).

1 Like

Start with something very simple:

main.cpp:

#include <Arduino.h>

void greeting(const char* name) {
  Serial.print("Hello ");
  Serial.print(name);
  Serial.println("!");
}

void setup() {
  Serial.begin(115200;
  greeting("circo_bruno");
}

void loop() {}

Then try outsourcing the greeting function to an .h/.cpp file.
Only include header files in the .h and .cpp file that are really needed.
This can be different in the .h and .cpp file.

In fact I’ve started decades ago, programming Z80 and 8085 in ASM, with direct calls for fixed address routines, frequently called by interruptions. Later I’ve been through Turbo Pascal and Turbo C to communicate via RS232 with these controllers.

A long time later I’ve been doing some projects with Microchip MCUs, but mostly focussing on the hardware itself, since I’m Electronics Engineer.

The resources offered by ESP32 brought me back to this world, making hardware projects for a colleague who was already very used to IoT applications in 2018. Unfortunatelly he has passed away due to COVID in 2021. Now I’m trying to continue the development of some products we’ve started together. Unfortunatelly I find myself a bit rusty and outdated in regard of cpp and object oriented.

But that’s what I’m trying to do: start it simple. I used to program with all procedures inside the same code file. But I’m aware this present project won’t start small and it will likely grow a lot and possibly very fast.

That’s why I took the decision of gathering knowledge to start it right. And your help is very precious. I’ve already learned a lot during these few hours. I’m very thankful.

This has been the best solution. Easy to implement. We’ve got to take care commenting out all pinnage setup and devices models definition lines.

In this present case, there was also a matter with this 2.5.43 version of TFT_eSPI library. It stucks during compillation because of missing definitions of half a dozen values. I’ve downgraded to 2.5.41 and how there’s no warning of missing definitions anymore.

Thank you very much.

Best regards,
Ciro