Need to make my library a bit more flexible

The following code is my attempt to place all HX8357 display and touchscreen code in a personal library that consists of mydisplay.h and mydisplay.cpp.

It is working, but there is a problem I have not yet solved. Some of my display projects use pins 7, 8, A2, and A3 for the Teensy to touchpad connections, and some others us A0, A1, A2, and A3.

I need to find a way to tell the library files which pins to use via the TeensyHX8357.ino, so I won’t need 2 different libraries.

Placing the pin definitions in the main sketch file, TeensyHX8357.ino, doesn’t work.

TeensyHX8357.ino

// for teensy 4.0
/***************************************************************************************************************/
#include "mydisplay.h"
#include <arduino.h>

float angle = 0;
int16_t h, v;
int16_t vLoc[SCREENWIDTH];
uint16_t fg = WHITE, bg = BLUE;

/***************************************************************************************************************/
void setup(void)
{
  Serial.begin(115200);
  delay(250);

  startDisplay(bg);
}

/***************************************************************************************************************/
void loop(void)
{
  Sine();
}

/***************************************************************************************************************/
void Sine()
{
  angle += 1;

  if (angle > 360)
    angle = 0;

  v = 160 + 128 * sin(angle * 0.0174532925);          // calculate the pixel vertical location

  if (h < SCREENWIDTH - 1)
  {
    h += 1;
    drawPixel(h, v, fg);                              // draw the pixel
    vLoc[h] = v;                                      // move the stored pixel vertical position
  }
  else
    Scroll();

  // delay(10);                                       // control the frequency here
}

/***************************************************************************************************************/
void Scroll()
{
  drawPixel(0, vLoc[0], bg);                          // Erase the zero pixel

  for (int x = 1; x < SCREENWIDTH; x++)
  {
    drawPixel(x - 1, vLoc[x], fg);                    // move the pixels backwards (vLoc[1 - MAX-1] to vLoc[0 - MAX-2])
    drawPixel(x, vLoc[x], bg);                        // erase the original pixels
    vLoc[x - 1] = vLoc[x];                            // move the stored pixels vertical position
  }

  vLoc[SCREENWIDTH - 1] = v;                                  // store last pixel vertical position
}

Mydisplay.cpp

#include "mydisplay.h"                          // display functions, touchscreen functions anda  button class

Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS_PIN, TFT_DC_PIN, TFT_RST_PIN);
TouchScreen tscr = TouchScreen(XP_PIN, YP_PIN, XM_PIN, YM_PIN, 500);

/***************************************************************************************************************/ 
void drawPixel(int x, int y, int clr)
{
  tft.drawPixel(x, y, clr);
}

/***************************************************************************************************************/ 
void rotation(int r)
{
  tft.setRotation(r);
}

/***************************************************************************************************************/ 
void startDisplay(int clr)
{
  tft.begin();
  delay(200);
  rotation(1);
  tft.fillScreen(clr);
}

Mydisplay.h

// display functions, touchscreen functions and a button class
#ifndef __DISPLAY_H__
#define __DISPLAY_H__

#include <arduino.h>
#include "Adafruit_HX8357.h"                        // the 480x320 display
#include <Adafruit_GFX.h>                           // Core graphics library
#include <TouchScreen.h>                            // the touch screen

void startDisplay(int clr = 0x0000);                // start the display with selected color
void drawPixel(int x, int y, int clr);

// The display uses hardware SPI, plus pins 9 & 10
#define TFT_DC_PIN    9
#define TFT_CS_PIN   10
#define TFT_RST_PIN  15                             // tie this to arduino RST if you like

// These are the four touchscreen pins
#define YM_PIN A0                                   // can be a digital pin
#define XP_PIN A1                                   // can be a digital pin
#define YP_PIN A2                                   // (pin 16) must be an analog pin, use "An" notation!
#define XM_PIN A3                                   // (pin 17) must be an analog pin, use "An" notation!

#define MINPRESSURE 100                             // minimum touch pressure to recognize a touch
#define MAXPRESSURE 1000                            // maximum touch pressure to recognize a touch

// This is calibration data for the raw touch data to align the screen coordinates
#define TS_MINX 105  // bottom
#define TS_MINY  60  // right
#define TS_MAXX 945  // top 
#define TS_MAXY 930  // left

#define SCREENWIDTH 480
#define SCREENHEIGHT 480

// Colors
#define BACKGROUND  0xA514
#define FILL        0xAD75
#define BLACK       0x0000
#define BLUE        0x0019
#define YELLOW      0xFFE0
#define RED         0xF800
#define DARKRED     0xA800
#define GREEN       0x0360
#define WHITE       0xFFFF
#define GRAY        0x9CD3

#endif

You could define the tft and tscr instances in your main sketch and pass these object by reference to the functions in your library.

Enclose these #defines by

#ifndef YM_PIN
  #define YM_PIN A0
  ...
#endif

to allow the user to specify a different set of pins.

The best way, is to adopt the modern C++ approach, make it a class, and pass the pin numbers either in the constructor, or the begin() method of the class.

It won't be hard to find examples, because 99.999% of libraries do it that way.

I'd go with the approach suggested by @jfjlaros. You'll note that the Adafruit_HX8357 class has 4 different constructors:

public:
  Adafruit_HX8357(int8_t _CS, int8_t _DC, int8_t _MOSI, int8_t _SCLK,
                  int8_t _RST, int8_t _MISO, uint8_t type = HX8357D);
  Adafruit_HX8357(int8_t _CS, int8_t _DC, int8_t _RST = -1,
                  uint8_t type = HX8357D);
#if !defined(ESP8266)
  Adafruit_HX8357(SPIClass *spi, int8_t _CS, int8_t _DC, int8_t _RST = -1,
                  uint8_t type = HX8357D);
#endif // end !ESP8266
  Adafruit_HX8357(tftBusWidth busWidth, int8_t d0, int8_t wr, int8_t dc,
                  int8_t cs = -1, int8_t rst = -1, int8_t rd = -1);

So, rather than supporting all of them, let the user create the Adafruit_HX8357 and pass it in by reference. For consistency, do the same with the TouchScreen object. Because you're using free functions rather than a class, you'll need to convert the references to global, static pointers for use by the functions. If you turn your library into a class, you can pass the object references to its constructor and use them to bind private references.

Illustrating why classes are preferred. Because global variables can collide, so need manual programmer management, or tools like name spaces or separate files. Awkward stuff like that, is why C++ is usually preferred over C.

True, agree. But at least declaring them 'static' confines them to one file.

I was lucky, I migrated to C++ before things got that hairy.

What is the "it" that I should make a class.

DrDiettrich, I tried using the #ifndef process, but that didn't work. The constructor in the .cpp file apparently does not see the #defines made in the .ino file, even if the #define is before the #include "mydisplay.h" statement.

Your existing functions become methods of the class. They might need a little bit of "massaging" to make them work there, but no semantic changes. The other "it" is the encapsulation of data in the class. It's all less complicated in reality, than it sounds.

That how it was done for C libraries ever since K met R.

My suggestion was for excluding any variables for pin numbers, so that no pin numbers have to be handed to the constructor(s). This had made the program a bit smaller and faster. But I don't remember how to make such simple constructs work in C/C++ :frowning:

I know. I worked briefly on some production C code for Y2K upgrades. But none of my programs ever needed more than one file, until I started Arduino programming.

Nothing really wrong with it, I guess, if you're used to it. It is definitely more "hands on" than the OO approach. I am also aware, sometimes you need it or it just seems easier that way.

The cause is simple. An #include statement in the .ino just causes a text merge. So it can't "push" any values "back" to the included file.

The preprocessor operations basically convert all your files into one giant file. Perhaps an oversimplification but it is the principle. The #includes then create a hierarchical structure, in this case the .ino is the root file.

The satisfactory solution for me was to move only the touchscreen constructor to the .ino file along with its 4 pin definitions. This allows me to leave all other existing code unchanged and still control the touchscreen pin defs from the .ino. When I use the touchscreen, I will just need to pass the tscr reference.

Thanks for all the info, ideas and suggestions.

Whatever works for you.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.