TFT_eSPI Library for LCD Modules on Boron - Troubleshooting

Hi! I am new to the forum but have done a little reading and appreciate how helpful this community seems to be!

I have 2x Borons that I am trying to setup with the following LCDs, one LCD for each Boron:

After doing a little digging, it looked like the TFT_eSPI library might be a good fit. However, I came across some issues including the library via the Web IDE, also described in a forum post in 2020. @peekay123 mentioned using the Workbench instead, which I attempted to do (still new to this) but came across an issue with functions in TFT-eSPI not being recognized (see screenshot below from an example I tried to verify):

So, this leads me to two questions that I could use some guidance on:

  1. Is there something I am missing from my workbench setup that is causing this lack of function recognition from the TFT_eSPI library?
  2. From a higher level, is there a better library to use or an alternative approach to setting up these LCDs on the Borons?

Thanks so much for the help and for the information in this community, I appreciate it!

@NickCallegari, which DeviceOS are you targetting? Are you using the WebIDE version of TFT_eSPI?

I am able to compile with TFT_eSPI version 2.3.89 I believe I got from Particle (cannot confirm) using DeviceOS 5.8.2 targetting a Boron 404x. To be honest, I'm not sure how you would create two separate TFT_eSPI objects for the two displays since there is only one setup file. I am not very familiar with TFT_eSPI so there may be a way.

Can you share Tester code?

Hi @peekay123, thanks for the quick reply, I appreciate it!

My Device OS target is set to 'Default (4.0.2)' currently when looking at the Devices tab of the WebIDE:

image

I tried using the WebIDE version of TFT_eSPI, but came across a similar problem as described in the forum post linked above where it wouldn't load, so pivoted to using the Workbench. To get the library to show up, I used the install Library command through the Workbench after creating a project:

Sounds like you have a library downloaded and had it running locally, maybe that is a better approach here?

Regarding two separate TFT_eSPI objects for the two displays, my intention was to have one set of files for one boron connected to one of the displays and a distinct set of files for the other boron connected to the other display, do you feel this could work?

Regarding tester code, I haven't coded anything myself yet since my first goal was to get an example working first, but included below is the example code I arbitrarily chose from the library to test verification:

// Adapted by Bodmer to work with a NodeMCU and ILI9341 or ST7735 display.
//
// This code currently does not "blink" the eye!
//
// Library used is here:
// https://github.com/Bodmer/TFT_eSPI
//
// To do, maybe, one day:
// 1. Get the eye to blink
// 2. Add another screen for another eye
// 3. Add varaible to set how wide open the eye is
// 4. Add a reflected highlight to the cornea
// 5. Add top eyelid shaddow to eye surface
// 6. Add aliasing to blur mask edge
//
// With one lidded eye drawn the code runs at 28-33fps (at 27-40MHz SPI clock)
// which is quite reasonable. Operation at an 80MHz SPI clock is possible but
// the display may not be able to cope with a clock rate that high and the
// performance improvement is small. Operate the ESP8266 at 160MHz for best
// frame rate. Note the images are stored in SPI FLASH (PROGMEM) so performance
// will be constrained by the increased memory access time.

// Original header for this sketch is below. Note: the technical aspects of the
// text no longer apply to this modified version of the sketch:
/*
//--------------------------------------------------------------------------
// Uncanny eyes for PJRC Teensy 3.1 with Adafruit 1.5" OLED (product #1431)
// or 1.44" TFT LCD (#2088).  This uses Teensy-3.1-specific features and
// WILL NOT work on normal Arduino or other boards!  Use 72 MHz (Optimized)
// board speed -- OLED does not work at 96 MHz.
//
// Adafruit invests time and resources providing this open source code,
// please support Adafruit and open-source hardware by purchasing products
// from Adafruit!
//
// Written by Phil Burgess / Paint Your Dragon for Adafruit Industries.
// MIT license.  SPI FIFO insight from Paul Stoffregen's ILI9341_t3 library.
// Inspired by David Boccabella's (Marcwolf) hybrid servo/OLED eye concept.
//--------------------------------------------------------------------------
*/

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

// Enable ONE of these #includes for the various eyes:
#include "defaultEye.h"        // Standard human-ish hazel eye
//#include "noScleraEye.h"       // Large iris, no sclera
//#include "dragonEye.h"         // Slit pupil fiery dragon/demon eye
//#include "goatEye.h"           // Horizontal pupil goat/Krampus eye


#define DISPLAY_DC      D3 // Data/command pin for BOTH displays
#define DISPLAY_RESET   D4 // Reset pin for BOTH displays
#define SELECT_L_PIN    D8 // LEFT eye chip select pin
#define SELECT_R_PIN    D8 // RIGHT eye chip select pin

// INPUT CONFIG (for eye motion -- enable or comment out as needed) --------

// The ESP8266 is rather constrained here as it only has one analogue port.
// An I2C ADC could be used for more analogue channels
//#define JOYSTICK_X_PIN A0 // Analog pin for eye horiz pos (else auto)
//#define JOYSTICK_Y_PIN A0 // Analog pin for eye vert position (")
//#define JOYSTICK_X_FLIP   // If set, reverse stick X axis
//#define JOYSTICK_Y_FLIP   // If set, reverse stick Y axis
#define TRACKING          // If enabled, eyelid tracks pupil
//#define IRIS_PIN       A0 // Photocell or potentiometer (else auto iris)
//#define IRIS_PIN_FLIP     // If set, reverse reading from dial/photocell
//#define IRIS_SMOOTH       // If enabled, filter input from IRIS_PIN
#define IRIS_MIN      140 // Clip lower analogRead() range from IRIS_PIN
#define IRIS_MAX      260 // Clip upper "
#define WINK_L_PIN      0 // Pin for LEFT eye wink button
#define BLINK_PIN       1 // Pin for blink button (BOTH eyes)
#define WINK_R_PIN      2 // Pin for RIGHT eye wink button
#define AUTOBLINK         // If enabled, eyes blink autonomously

// Probably don't need to edit any config below this line, -----------------
// unless building a single-eye project (pendant, etc.), in which case one
// of the two elements in the eye[] array further down can be commented out.

// Eye blinks are a tiny 3-state machine.  Per-eye allows winks + blinks.
#define NOBLINK 0     // Not currently engaged in a blink
#define ENBLINK 1     // Eyelid is currently closing
#define DEBLINK 2     // Eyelid is currently opening
typedef struct {
  int8_t   pin;       // Optional button here for indiv. wink
  uint8_t  state;     // NOBLINK/ENBLINK/DEBLINK
  int32_t  duration;  // Duration of blink state (micros)
  uint32_t startTime; // Time (micros) of last state change
} eyeBlink;

struct {
  TFT_eSPI tft; // OLED/eye[e].tft object
  uint8_t     cs;      // Chip select pin
  eyeBlink    blink;   // Current blink state
} eye[] = { // OK to comment out one of these for single-eye display:
  TFT_eSPI(),SELECT_L_PIN,{WINK_L_PIN,NOBLINK},
  //TFT_eSPI(),SELECT_R_PIN,{WINK_R_PIN,NOBLINK},
};

#define NUM_EYES (sizeof(eye) / sizeof(eye[0]))

uint32_t fstart = 0;  // start time to improve frame rate calculation at startup

// INITIALIZATION -- runs once at startup ----------------------------------

void setup(void) {
  uint8_t e = 0;
  
  Serial.begin(250000);
  randomSeed(analogRead(A0)); // Seed random() from floating analog input

  eye[e].tft.init();
  eye[e].tft.fillScreen(TFT_BLACK);
  eye[e].tft.setRotation(0);

  fstart = millis()-1; // Subtract 1 to avoid divide by zero later
}


// EYE-RENDERING FUNCTION --------------------------------------------------
#define BUFFER_SIZE 256 // 64 to 512 seems optimum = 30 fps for default eye
void drawEye( // Renders one eye.  Inputs must be pre-clipped & valid.
  // Use native 32 bit variables where possible as this is 10% faster!
  uint8_t  e,       // Eye array index; 0 or 1 for left/right
  uint32_t iScale,  // Scale factor for iris
  uint32_t  scleraX, // First pixel X offset into sclera image
  uint32_t  scleraY, // First pixel Y offset into sclera image
  uint32_t  uT,      // Upper eyelid threshold value
  uint32_t  lT) {    // Lower eyelid threshold value

  uint32_t  screenX, screenY, scleraXsave;
  int32_t  irisX, irisY;
  uint32_t p, a;
  uint32_t d;

  uint32_t pixels = 0;
  uint16_t pbuffer[BUFFER_SIZE]; // This one needs to be 16 bit
  
  // Set up raw pixel dump to entire screen.  Although such writes can wrap
  // around automatically from end of rect back to beginning, the region is
  // reset on each frame here in case of an SPI glitch.

  //eye[e].tft.setAddrWindow(319-127, 0, 319, 127);
  eye[e].tft.setAddrWindow(0, 0, 127, 127);
  
  //digitalWrite(eye[e].cs, LOW);                       // Chip select

  // Now just issue raw 16-bit values for every pixel...

  scleraXsave = scleraX; // Save initial X value to reset on each line
  irisY       = scleraY - (SCLERA_HEIGHT - IRIS_HEIGHT) / 2;
  for(screenY=0; screenY<SCREEN_HEIGHT; screenY++, scleraY++, irisY++) {
    scleraX = scleraXsave;
    irisX   = scleraXsave - (SCLERA_WIDTH - IRIS_WIDTH) / 2;
    for(screenX=0; screenX<SCREEN_WIDTH; screenX++, scleraX++, irisX++) {
      if((pgm_read_byte(lower + screenY * SCREEN_WIDTH + screenX) <= lT) ||
         (pgm_read_byte(upper + screenY * SCREEN_WIDTH + screenX) <= uT)) {             // Covered by eyelid
        p = 0;
      } else if((irisY < 0) || (irisY >= IRIS_HEIGHT) ||
                (irisX < 0) || (irisX >= IRIS_WIDTH)) { // In sclera
        p = pgm_read_word(sclera + scleraY * SCLERA_WIDTH + scleraX);
      } else {                                          // Maybe iris...
        p = pgm_read_word(polar + irisY * IRIS_WIDTH + irisX);                        // Polar angle/dist
        d = (iScale * (p & 0x7F)) / 128;                // Distance (Y)
        if(d < IRIS_MAP_HEIGHT) {                       // Within iris area
          a = (IRIS_MAP_WIDTH * (p >> 7)) / 512;        // Angle (X)
          p = pgm_read_word(iris + d * IRIS_MAP_WIDTH + a);                               // Pixel = iris
        } else {                                        // Not in iris
          p = pgm_read_word(sclera + scleraY * SCLERA_WIDTH + scleraX);                 // Pixel = sclera
        }
      }
      *(pbuffer + pixels++) = p>>8 | p<<8;

      if (pixels >= BUFFER_SIZE) { yield(); eye[e].tft.pushColors((uint8_t*)pbuffer, pixels*2); pixels = 0;}
    }
  }

   if (pixels) { eye[e].tft.pushColors(pbuffer, pixels); pixels = 0;}
}


// EYE ANIMATION -----------------------------------------------------------

const uint8_t ease[] = { // Ease in/out curve for eye movements 3*t^2-2*t^3
    0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1,  2,  2,  2,  3,   // T
    3,  3,  4,  4,  4,  5,  5,  6,  6,  7,  7,  8,  9,  9, 10, 10,   // h
   11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23,   // x
   24, 25, 26, 27, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39,   // 2
   40, 41, 42, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 56, 57, 58,   // A
   60, 61, 62, 63, 65, 66, 67, 69, 70, 72, 73, 74, 76, 77, 78, 80,   // l
   81, 83, 84, 85, 87, 88, 90, 91, 93, 94, 96, 97, 98,100,101,103,   // e
  104,106,107,109,110,112,113,115,116,118,119,121,122,124,125,127,   // c
  128,130,131,133,134,136,137,139,140,142,143,145,146,148,149,151,   // J
  152,154,155,157,158,159,161,162,164,165,167,168,170,171,172,174,   // a
  175,177,178,179,181,182,183,185,186,188,189,190,192,193,194,195,   // c
  197,198,199,201,202,203,204,205,207,208,209,210,211,213,214,215,   // o
  216,217,218,219,220,221,222,224,225,226,227,228,228,229,230,231,   // b
  232,233,234,235,236,237,237,238,239,240,240,241,242,243,243,244,   // s
  245,245,246,246,247,248,248,249,249,250,250,251,251,251,252,252,   // o
  252,253,253,253,254,254,254,254,254,255,255,255,255,255,255,255 }; // n

#ifdef AUTOBLINK
uint32_t timeOfLastBlink = 0L, timeToNextBlink = 0L;
#endif

void frame( // Process motion for a single frame of left or right eye
  uint32_t        iScale) {     // Iris scale (0-1023) passed in
  static uint32_t frames   = 0; // Used in frame rate calculation
  static uint8_t  eyeIndex = 0; // eye[] array counter
  int32_t         eyeX, eyeY;
  uint32_t        t = micros(); // Time at start of function

  Serial.print((++frames * 1000) / (millis() - fstart)); Serial.println("fps");// Show frame rate

  if(++eyeIndex >= NUM_EYES) eyeIndex = 0; // Cycle through eyes, 1 per call

 // Autonomous X/Y eye motion
      // Periodically initiates motion to a new random point, random speed,
      // holds there for random period until next motion.

  static boolean  eyeInMotion      = false;
  static int32_t  eyeOldX=512, eyeOldY=512, eyeNewX=512, eyeNewY=512;
  static uint32_t eyeMoveStartTime = 0L;
  static int32_t  eyeMoveDuration  = 0L;

  int32_t dt = t - eyeMoveStartTime;      // uS elapsed since last eye event
  if(eyeInMotion) {                       // Currently moving?
    if(dt >= eyeMoveDuration) {           // Time up?  Destination reached.
      eyeInMotion      = false;           // Stop moving
      eyeMoveDuration  = random(3000000L); // 0-3 sec stop
      eyeMoveStartTime = t;               // Save initial time of stop
      eyeX = eyeOldX = eyeNewX;           // Save position
      eyeY = eyeOldY = eyeNewY;
    } else { // Move time's not yet fully elapsed -- interpolate position
      int16_t e = ease[255 * dt / eyeMoveDuration] + 1;   // Ease curve
      eyeX = eyeOldX + (((eyeNewX - eyeOldX) * e) / 256); // Interp X
      eyeY = eyeOldY + (((eyeNewY - eyeOldY) * e) / 256); // and Y
    }
  } else {                                // Eye stopped
    eyeX = eyeOldX;
    eyeY = eyeOldY;
    if(dt > eyeMoveDuration) {            // Time up?  Begin new move.
      int16_t  dx, dy;
      uint32_t d;
      do {                                // Pick new dest in circle
        eyeNewX = random(1024);
        eyeNewY = random(1024);
        dx      = (eyeNewX * 2) - 1023;
        dy      = (eyeNewY * 2) - 1023;
      } while((d = (dx * dx + dy * dy)) > (1023 * 1023)); // Keep trying
      eyeMoveDuration  = random(50000, 150000);//random(72000, 144000); // ~1/14 - ~1/7 sec
      eyeMoveStartTime = t;               // Save initial time of move
      eyeInMotion      = true;            // Start move on next frame
    }
  }

  // Blinking
/*
#ifdef AUTOBLINK
  // Similar to the autonomous eye movement above -- blink start times
  // and durations are random (within ranges).
  if((t - timeOfLastBlink) >= timeToNextBlink) { // Start new blink?
    timeOfLastBlink = t;
    uint32_t blinkDuration = random(36000, 72000); // ~1/28 - ~1/14 sec
    // Set up durations for both eyes (if not already winking)
    for(uint8_t e=0; e<NUM_EYES; e++) {
      if(eye[e].blink.state == NOBLINK) {
        eye[e].blink.state     = ENBLINK;
        eye[e].blink.startTime = t;
        eye[e].blink.duration  = blinkDuration;
      }
    }
    timeToNextBlink = blinkDuration * 3 + random(4000000);
  }
#endif
*/
/*
  if(eye[eyeIndex].blink.state) { // Eye currently blinking?
    // Check if current blink state time has elapsed
    if((t - eye[eyeIndex].blink.startTime) >= eye[eyeIndex].blink.duration) {
      // Yes -- increment blink state, unless...
      if((eye[eyeIndex].blink.state == ENBLINK) &&  // Enblinking and...
        ((digitalRead(BLINK_PIN) == LOW) ||         // blink or wink held...
          digitalRead(eye[eyeIndex].blink.pin) == LOW)) {
        // Don't advance state yet -- eye is held closed instead
      } else { // No buttons, or other state...
        if(++eye[eyeIndex].blink.state > DEBLINK) { // Deblinking finished?
          eye[eyeIndex].blink.state = NOBLINK;      // No longer blinking
        } else { // Advancing from ENBLINK to DEBLINK mode
          eye[eyeIndex].blink.duration *= 2; // DEBLINK is 1/2 ENBLINK speed
          eye[eyeIndex].blink.startTime = t;
        }
      }
    }
  } else { // Not currently blinking...check buttons!
    if(digitalRead(BLINK_PIN) == LOW) {
      // Manually-initiated blinks have random durations like auto-blink
      uint32_t blinkDuration = random(36000, 72000);
      for(uint8_t e=0; e<NUM_EYES; e++) {
        if(eye[e].blink.state == NOBLINK) {
          eye[e].blink.state     = ENBLINK;
          eye[e].blink.startTime = t;
          eye[e].blink.duration  = blinkDuration;
        }
      }
    } else if(digitalRead(eye[eyeIndex].blink.pin) == LOW) { // Wink!
      eye[eyeIndex].blink.state     = ENBLINK;
      eye[eyeIndex].blink.startTime = t;
      eye[eyeIndex].blink.duration  = random(45000, 90000);
    }
  }
*/
  // Process motion, blinking and iris scale into renderable values

  // Iris scaling: remap from 0-1023 input to iris map height pixel units
  iScale = ((IRIS_MAP_HEIGHT + 1) * 1024) /
           (1024 - (iScale * (IRIS_MAP_HEIGHT - 1) / IRIS_MAP_HEIGHT));

  // Scale eye X/Y positions (0-1023) to pixel units used by drawEye()
  eyeX = map(eyeX, 0, 1023, 0, SCLERA_WIDTH  - 128);
  eyeY = map(eyeY, 0, 1023, 0, SCLERA_HEIGHT - 128);
  if(eyeIndex == 1) eyeX = (SCLERA_WIDTH - 128) - eyeX; // Mirrored display

  // Horizontal position is offset so that eyes are very slightly crossed
  // to appear fixated (converged) at a conversational distance.  Number
  // here was extracted from my posterior and not mathematically based.
  // I suppose one could get all clever with a range sensor, but for now...
  eyeX += 4;
  if(eyeX > (SCLERA_WIDTH - 128)) eyeX = (SCLERA_WIDTH - 128);

  // Eyelids are rendered using a brightness threshold image.  This same
  // map can be used to simplify another problem: making the upper eyelid
  // track the pupil (eyes tend to open only as much as needed -- e.g. look
  // down and the upper eyelid drops).  Just sample a point in the upper
  // lid map slightly above the pupil to determine the rendering threshold.
  static uint8_t uThreshold = 128;
  uint8_t        lThreshold, n;

#ifdef TRACKING
  int16_t sampleX = SCLERA_WIDTH  / 2 - (eyeX / 2), // Reduce X influence
          sampleY = SCLERA_HEIGHT / 2 - (eyeY + IRIS_HEIGHT / 4);
  // Eyelid is slightly asymmetrical, so two readings are taken, averaged
  if(sampleY < 0) n = 0;
  else            n = (pgm_read_byte(upper + sampleY * SCREEN_WIDTH + sampleX) +
                       pgm_read_byte(upper + sampleY * SCREEN_WIDTH + (SCREEN_WIDTH - 1 - sampleX))) / 2;
  uThreshold = (uThreshold * 3 + n) / 4; // Filter/soften motion
  // Lower eyelid doesn't track the same way, but seems to be pulled upward
  // by tension from the upper lid.
  lThreshold = 254 - uThreshold;
#else // No tracking -- eyelids full open unless blink modifies them
  uThreshold = lThreshold = 0;
#endif

  // The upper/lower thresholds are then scaled relative to the current
  // blink position so that blinks work together with pupil tracking.
  if(eye[eyeIndex].blink.state) { // Eye currently blinking?
    uint32_t s = (t - eye[eyeIndex].blink.startTime);
    if(s >= eye[eyeIndex].blink.duration) s = 255;   // At or past blink end
    else s = 255 * s / eye[eyeIndex].blink.duration; // Mid-blink
    s          = (eye[eyeIndex].blink.state == DEBLINK) ? 1 + s : 256 - s;
    n          = (uThreshold * s + 254 * (257 - s)) / 256;
    lThreshold = (lThreshold * s + 254 * (257 - s)) / 256;
  } else {
    n          = uThreshold;
  }

  // Pass all the derived values to the eye-rendering function:
  drawEye(eyeIndex, iScale, eyeX, eyeY, n, lThreshold);

}


// AUTONOMOUS IRIS SCALING (if no photocell or dial) -----------------------

#if !defined(IRIS_PIN) || (IRIS_PIN < 0)

// Autonomous iris motion uses a fractal behavior to similate both the major
// reaction of the eye plus the continuous smaller adjustments that occur.

uint16_t oldIris = (IRIS_MIN + IRIS_MAX) / 2, newIris;

void split( // Subdivides motion path into two sub-paths w/randimization
  int16_t  startValue, // Iris scale value (IRIS_MIN to IRIS_MAX) at start
  int16_t  endValue,   // Iris scale value at end
  uint32_t startTime,  // micros() at start
  int32_t  duration,   // Start-to-end time, in microseconds
  int16_t  range) {    // Allowable scale value variance when subdividing

  if(range >= 8) {     // Limit subdvision count, because recursion
    range    /= 2;     // Split range & time in half for subdivision,
    duration /= 2;     // then pick random center point within range:
    int16_t  midValue = (startValue + endValue - range) / 2 + random(range);
    uint32_t midTime  = startTime + duration;
    split(startValue, midValue, startTime, duration, range); // First half
    split(midValue  , endValue, midTime  , duration, range); // Second half
  } else {             // No more subdivisons, do iris motion...
    int32_t dt;        // Time (micros) since start of motion
    int16_t v;         // Interim value
    while((dt = (micros() - startTime)) < duration) {
      v = startValue + (((endValue - startValue) * dt) / duration);
      if(v < IRIS_MIN)      v = IRIS_MIN; // Clip just in case
      else if(v > IRIS_MAX) v = IRIS_MAX;
      frame(v);        // Draw frame w/interim iris scale value
    }
  }
}

#endif // !IRIS_PIN


// MAIN LOOP -- runs continuously after setup() ----------------------------

void loop() {

#if defined(IRIS_PIN) && (IRIS_PIN >= 0) // Interactive iris

  uint16_t v = 512; //analogRead(IRIS_PIN);       // Raw dial/photocell reading
#ifdef IRIS_PIN_FLIP
  v = 1023 - v;
#endif
  v = map(v, 0, 1023, IRIS_MIN, IRIS_MAX); // Scale to iris range
#ifdef IRIS_SMOOTH // Filter input (gradual motion)
  static uint16_t irisValue = (IRIS_MIN + IRIS_MAX) / 2;
  irisValue = ((irisValue * 15) + v) / 16;
  frame(irisValue);
#else // Unfiltered (immediate motion)
  frame(v);
#endif // IRIS_SMOOTH

#else  // Autonomous iris scaling -- invoke recursive function

  newIris = random(IRIS_MIN, IRIS_MAX);
  split(oldIris, newIris, micros(), 10000000L, IRIS_MAX - IRIS_MIN);
  oldIris = newIris;

#endif // IRIS_PIN

//screenshotToConsole();
}




Thanks so again for the help, happy to provide anything else that might be helpful!

@NickCallegari, the WebIDE has a long-standing issue with displaying libraries with a large set of files, of which the TFT_eSPI is one of them.

If I understand correctly, you basically want the ST7789V2 on one Boron and the GC9A01 on another Boron. You can do this using two different projects in Workbench.

I would start by getting the WebIDE version of the TFT_eSPI library into your project by installing it on a correctly created Workbench project if you haven't already done so. You may want to use a simpler example since the once you have assume two displays are connected. Assuming the first project is the the ST7789V2, you will need to modify the User_Setup.h file in the TFT_eSPI library src folder to select the correct display driver. For the other project using the GC9A01, you would do the same in that project's TFT_eSPI lib directory.

1 Like

Hi @peekay123, thanks again for the quick reply, you are understanding correctly my intent with the multiple Borons :slight_smile:

So, I created a new project to start fresh, and with the new project, I used the same method outlined above to install the 'TFT_eSPI' library. This time, after hitting verify, I had successful compilation. Following this, I modified the User_Setup.h file as directed, starting with the GC9A01 driver LCD. I modified the file based on a comment by 'Digi' on the amazon purchase page, included below (for others reading this thread in the future to reference):

/*Below is the minimum config (User_Setup.h) you can use for this display and it will work. Change PIN numbers to suit your needs for your ESP module.
#define GC9A01_DRIVER*/

// For CYD ESP32-2432S028
#define TFT_WIDTH 240
#define TFT_HEIGHT 240

// For ESP32 Dev board (only tested with GC9A01 display)
// The hardware SPI can be mapped to any pins

#define TFT_MOSI 21 //SDA In some display driver board, it might be written as "SDA" and so on.
#define TFT_SCLK 22 //SCL
#define TFT_CS 16 //CS Chip select control pin
#define TFT_DC 17 //DC Data Command control pin
#define TFT_RST 4 //RST Reset pin (could connect to Arduino RESET pin)

// Comment out the #defines below with // to stop that font being loaded
// The ESP8366 and ESP32 have plenty of memory so commenting out fonts is not
// normally necessary. If all fonts are loaded the extra FLASH space required is
// about 17Kbytes. To save FLASH space only enable the fonts you need!

#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:-.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts

// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
#define SMOOTH_FONT

// Define the SPI clock frequency, this affects the graphics rendering speed. Too
// fast and the TFT driver will not keep up and display corruption appears.
// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines)
// With an ILI9163 display 27 MHz works OK.

// #define SPI_FREQUENCY 1000000
// #define SPI_FREQUENCY 5000000
//#define SPI_FREQUENCY 10000000
//#define SPI_FREQUENCY 20000000
//#define SPI_FREQUENCY 27000000
#define SPI_FREQUENCY 40000000
//#define SPI_FREQUENCY 55000000 // STM32 SPI1 only (SPI2 maximum is 27MHz)
// #define SPI_FREQUENCY 80000000

Following this User_Setup.h modification, I decided to run the 'TFT_Clock.ino' example. I chose this one because it is much simpler than the previous example chosen and requires only one LCD (A note for users new to Workbench like me, make sure to set your device model and deviceID before flashing, this can be seen at the bottom of the workbench window):

Successfully flashed, progress! I am now a little lost on the pin mapping though, although this should be an easy fix. Here is what I have so far, which is not working:

LCD RST Pin --> Boron RST Pin
LCD CS Pin --> A4
LCD DC Pin --> A5
LCD SDA Pin --> Boron SDA Pin
LCD SCL Pin --> Boron SCL Pin
LCD GND Pin --> Boron GND Pin
LCD VCC Pin --> Boron 3V3 Pin

And here is what is what I tweaked in the User_Setup/h file, which is different from the 'Digi' code above since I had to map pins. It's worth noting that I set defined pins with no number on the Boron (i.e., RST) as '-1', but this might be wildly incorrect:

And here is an image of the setup, the screen is slightly backlit when plugged in:

Sorry these posts are a bit long, I am trying my best to be thorough so others that run into similar issues in the future can work through their troubleshooting as well, thanks!

@NickCallegari, the LCD reset line goes to a GPIO but is not mandatory. Connecting it to the Boron reset line will not do what you want.

The recommendations you got are incorrect as far as I can tell. The CS and DC pins on A4 and A5 are fine. The displays SDA and SCL are acctually MOSI and SCK which are SPI pins. The SDA and SCL pins are for I2C! I have no idea why some vendors use the I2C nomenclature for SPI displays!!!

Specifying TFT_MOSI, TFT_SCLK as -1 means you will be using hardware SPI and TFT_RST as -1 means you won't be using that pin.

Here is how your connections should go:

LCD RST Pin --> Not Connected
LCD CS Pin --> A4
LCD DC Pin --> A5
LCD SDA Pin --> Boron MOSI pin
LCD SCL Pin --> Boron SCK Pin
LCD GND Pin --> Boron GND Pin
LCD VCC Pin --> Boron 3V3 Pin
1 Like

Thanks for the info, @peekay123, definintely an interesting choice by vendors to use that nomenclature!

I rewired my setup as you specified, here is what it looks like now:

After flashing again, I unfortunately had no luck in getting the display to work appropriately. I originally tried this pin mapping in the User_Setup.h file:

Which may or may not have been the problem. In my debugging quest, I looked up the pin mapping for the boron and remapped the pins based on their corresponding pin assignments:

This also didn't work (although, again, not sure if this was the problem in the first place). I then became suspicious of whether the .ino file was actually flashing to my Boron, I tried both local flashing and cloud flashing via Workbench, but still no luck. It is worth noting that I have been trying to flash an example directly from the library (as shown below), not an .ino file in the 'src' folder. Maybe this is the problem?

Thanks again for the help, sorry I am still learning here!

For reference, here is the User_Setup.h code:

//                            USER DEFINED SETTINGS
//   Set driver type, fonts to be loaded, pins used and SPI control method etc
//
//   See the User_Setup_Select.h file if you wish to be able to define multiple
//   setups and then easily select which setup file is used by the compiler.
//
//   If this file is editted correctly then all the library example sketches should
//   run without the need to make any more changes for a particular hardware setup!

// ##################################################################################
//
// Section 0. Call up the right driver file and any options for it
//
// ##################################################################################

// Use ESP32 Parallel interface instead of SPI
#define ESP32_PARALLEL

// For CYD ESP32-2432S028
#define TFT_WIDTH 240
#define TFT_HEIGHT 240

#define TFT_MOSI 12 //SDA In some display driver board, it might be written as "SDA" and so on.
#define TFT_SCLK 11 //SCL
#define TFT_CS 9 //CS Chip select control pin
#define TFT_DC 10 //DC Data Command control pin
#define TFT_RST -1 //RST Reset pin (could connect to Arduino RESET pin)

// Only define one driver, the other ones must be commented out
//#define ILI9341_DRIVER
//#define ST7735_DRIVER
//#define ILI9163_DRIVER
//#define S6D02A1_DRIVER
//#define RPI_ILI9486_DRIVER // 20MHz maximum SPI
#define GC9A01_DRIVER

// For ST7735 and ILI9163 ONLY, define the pixel width and height in portrait orientation
//#define TFT_WIDTH  128
//#define TFT_HEIGHT 160
//#define TFT_HEIGHT 128

// For ST7735 ONLY, define the type of display, originally this was based on the
// colour of the tab on the screen protector film but this is not always true, so try
// out the different options below if the screen does not display graphics correctly,
// e.g. colours wrong, mirror images, or tray pixels at the edges.
// Comment out ALL BUT ONE of these options for a ST7735 display driver, save this
// this User_Setup file, then rebuild and upload the sketch to the board again:

//#define ST7735_INITB
//#define ST7735_GREENTAB
//#define ST7735_GREENTAB2
//#define ST7735_GREENTAB3
//#define ST7735_GREENTAB128 // For 128 x 128 display
//#define ST7735_REDTAB
//#define ST7735_BLACKTAB

// ##################################################################################
//
// Section 1. Define the pins that are used to interface with the display here
//
// ##################################################################################

// We must use hardware SPI, a minimum of 3 GPIO pins is needed.
// Typical setup for NodeMCU ESP-12 is :
//
// Display SDO/MISO  to NodeMCU pin D6 (or leave disconnected if not reading TFT)
// Display LED       to NodeMCU pin VIN (or 5V, see below)
// Display SCK       to NodeMCU pin D5
// Display SDI/MOSI  to NodeMCU pin D7
// Display DC (RS/AO)to NodeMCU pin D3
// Display RESET     to NodeMCU pin D4 (or RST, see below)
// Display CS        to NodeMCU pin D8 (or GND, see below)
// Display GND       to NodeMCU pin GND (0V)
// Display VCC       to NodeMCU 5V or 3.3V
//
// The TFT RESET pin can be connected to the NodeMCU RST pin or 3.3V to free up a control pin
//
// The DC (Data Command) pin may be labelled AO or RS (Register Select)
//
// With some displays such as the ILI9341 the TFT CS pin can be connected to GND if no more
// SPI deivces (e.g. an SD Card) are connected, in this case comment out the #define TFT_CS
// line below so it is NOT defined. Other displays such at the ST7735 require the TFT CS pin
// to be toggled during setup, so in these cases the TFT_CS line must be defined and connected.
//
// The NodeMCU D0 pin can be used for RST
//
// See Section 2. below if DC or CS is connected to D0
//
// Note: only some versions of the NodeMCU provide the USB 5V on the VIN pin
// If 5V is not available at a pin you can use 3.3V but backlight brightness
// will be lower.

// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP8266 SETUP ######

// For ModeMCU - use pin numbers in the form PIN_Dx where Dx is the NodeMCU pin designation

//#define TFT_CS   2  // Chip select control pin D8
//#define TFT_DC   4  // Data Command control pin
//#define TFT_RST  0  // Reset pin (could connect to NodeMCU RST, see next line)

//#define TFT_WR	 5  
//#define TFT_RD   21      // /RD signal connected to Arduino digital pin 32
//#define TFT_D0   12
//#define TFT_D1   13
//#define TFT_D2   14
//#define TFT_D3   15
//#define TFT_D4   16
//#define TFT_D5   17
//#define TFT_D6   18
//#define TFT_D7   19

//#define TFT_RST  -1  // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V

//#define TFT_WR PIN_D2    // Write strobe for modified Raspberry Pi TFT only

// ###### EDIT THE PIN NUMBERS IN THE LINES FOLLOWING TO SUIT YOUR ESP32 SETUP   ######

// For ESP32 Dev board (only tested with ILI9341 display)
// The hardware SPI can be mapped to any pins

//#define TFT_MISO 19
//#define TFT_MOSI 23
//#define TFT_SCLK 18
//#define TFT_CS    5  // Chip select control pin
//#define TFT_DC    2  // Data Command control pin
//#define TFT_RST   4  // Reset pin (could connect to RST pin)
//#define TFT_RST  -1  // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST

// ##################################################################################
//
// Section 2. Define the way the DC and/or CS lines are driven (ESP8266 only)
//
// ##################################################################################

// Normally the library uses direct register access for the DC and CS lines for speed
// If D0 (GPIO16) is used for CS or DC then a different slower method must be used
// Uncomment one line if D0 is used for DC or CS
// DC on D0 = 6% performance penalty at 40MHz SPI running graphics test
// CS on D0 = 2% performance penalty at 40MHz SPI running graphics test

// #define D0_USED_FOR_DC
// #define D0_USED_FOR_CS

// ##################################################################################
//
// Section 3. Define the fonts that are to be used here
//
// ##################################################################################

// Comment out the #defines below with // to stop that font being loaded
// The ESP8366 had plenty of memory so commenting out fonts is not normally necessary
// If all fonts are loaded the extra FLASH space required is about 17Kbytes...
// To save FLASH space only enable the fonts you need!

#define LOAD_GLCD   // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2  // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4  // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6  // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7  // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
#define LOAD_FONT8  // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
#define LOAD_GFXFF  // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
// Comment out the #define below to stop the SPIFFS filing system and smooth font code being loaded
// this will save ~20kbytes of FLASH
#define SMOOTH_FONT
// ##################################################################################
//
// Section 4. Not used
//
// ##################################################################################


// ##################################################################################
//
// Section 5. Other options
//
// ##################################################################################

// Define the SPI clock frequency
// With an ILI9341 display 40MHz works OK, 80MHz sometimes fails
// With a ST7735 display more than 27MHz may not work (spurious pixels and lines)
// With an ILI9163 display TBD MHz works OK,

// #define SPI_FREQUENCY   1000000
// #define SPI_FREQUENCY   5000000
// #define SPI_FREQUENCY  10000000
// #define SPI_FREQUENCY  20000000
// #define SPI_FREQUENCY  27000000 // Actually sets it to 26.67MHz = 80/3
   #define SPI_FREQUENCY  40000000 // Maximum to use SPIFFS
// #define SPI_FREQUENCY  80000000


// Comment out the following #define if "SPI Transactions" do not need to be
// supported. Tranaction support is required if other SPI devices are connected.
// When commented out the code size will be smaller and sketches will
// run slightly faster, so leave it commented out unless you need it!
// Transaction support is needed to work with SD library but not needed with TFT_SdFat

// #define SUPPORT_TRANSACTIONS

@NickCallegari, in the User_Setup.h file you need to:

  • Comment out this line at the top:
    #define ESP32_PARALLEL

  • The pins should be:

#define TFT_MOSI -1 //SDA In some display driver board, it might be written as "SDA" and so on.
#define TFT_SCLK  SCK
#define TFT_CS A4 //CS Chip select control pin
#define TFT_DC A5 //DC Data Command control pin
#define TFT_RST -1 //RST Reset pin (could connect to Arduino RESET pin)
  • Use #define SPI_FREQUENCY 20000000 instead of #define SPI_FREQUENCY 40000000

The MOSI and SCK pins are defined in hardware by default. The pin definitions are there because the are used with the ESP32. So you need to set them to -1.

This makes sense, thanks for the support @peekay123! I made the edits as suggested and still haven't gotten the LCD to work, I had a realization though: The driver that the User_Setup.h defines (#define GC9A01_DRIVER) is not actually an included driver in the TFT_eSPI library...

I found the driver (I think) on github via freemanzwin, which includes a variety of .h and .cpp files, along with edits to some of the existing .h and .cpp files in the TFT_eSPI library, to implement the driver.

I started implementing the changes and should have an update tomorrow morning, thanks again for the help!

1 Like

Hello, so I've been struggling with this some more and after a variety of unsuccessful attempts, I have returned (but haven't given up).

I implemented the extensive changes from the github above and had no errors compiling on my first try (seemed suspicious), but also did not have a response on the LCD screen. After a little troubleshooting with serial monitor, I determined that I was not flashing the example code, but a blank .ino file for the project I created...

After copying the 'TFT_Clock.ino' code into the project .ino file and compiling, I returned back to my original workbench issue of 'TFT_eSPI.h' not having its functions recognized. I started a fresh project using the method described by @peekay123 above and used the same method to install the 'TFT_eSPI' library, but still no luck.

I then took slightly drastic measures and moved files around so they were all in the same location on the file tree (moved the .ino and .cpp files for my program into the library and deleted the empty src folder), as shown below:

This leads to my next error, which I am assuming is because the TFT_eSPI is finally recognized: 'pgmspace.h' not being recognized

I then did some research and learned that the 'Arduino.h' can be a more comprehensive substitute for 'pgmspace.h' but this was based on a comment by @ScruffR in a 2018 post, so I might have misinterpreted (trying my best to scour forums first before posting here). I then replaced all the calls for '#include <pgmspace.h>' with '#include <Arduino.h>', which were mainly located in the Fonts folder. A little nuance here, I had to change the C (.c) files in the fonts folder to C++ (cpp) to get this to work.

After this, I came across the following error:

I was able to get the TFT_eSPI.h file to be recognized after some more file movement, but it just let to some more files not being recognized, most notably soc/spi_reg.h not being recognized.

My hunch is that I am going down a rabbit hole and the solution is simpler from a higher level. @peekay123, would it be possible for you to try and compile the 'TFT_Clock.ino' file on your end? If it compiles, then it's likely not a problem with the library, but something on my end (which is the hope!)

Thanks so much again for the help, I appreciate it! Here is the TFT_Clock.ino code for reference as well:

/*
 An example analogue clock using a TFT LCD screen to show the time
 use of some of the drawing commands with the ST7735 library.

 For a more accurate clock, it would be better to use the RTClib library.
 But this is just a demo. 

 Uses compile time to set the time so a reset will start with the compile time again
 
 Gilchrist 6/2/2014 1.0
 Updated by Bodmer
 */

#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
#include <SPI.h>

TFT_eSPI tft = TFT_eSPI();  // Invoke library, pins defined in User_Setup.h

#define TFT_GREY 0xBDF7

float sx = 0, sy = 1, mx = 1, my = 0, hx = -1, hy = 0;    // Saved H, M, S x & y multipliers
float sdeg=0, mdeg=0, hdeg=0;
uint16_t osx=64, osy=64, omx=64, omy=64, ohx=64, ohy=64;  // Saved H, M, S x & y coords
uint16_t x0=0, x1=0, yy0=0, yy1=0;
uint32_t targetTime = 0;                    // for next 1 second timeout

static uint8_t conv2d(const char* p) {
  uint8_t v = 0;
  if ('0' <= *p && *p <= '9')
    v = *p - '0';
  return 10 * v + *++p - '0';
}

uint8_t hh=conv2d(__TIME__), mm=conv2d(__TIME__+3), ss=conv2d(__TIME__+6);  // Get H, M, S from compile time

boolean initial = 1;

void setup(void) {
  tft.init();
  tft.setRotation(0);
  tft.fillScreen(TFT_GREY);
  tft.setTextColor(TFT_GREEN, TFT_GREY);  // Adding a black background colour erases previous text automatically
  
  // Draw clock face
  tft.fillCircle(64, 64, 61, TFT_BLUE);
  tft.fillCircle(64, 64, 57, TFT_BLACK);

  // Draw 12 lines
  for(int i = 0; i<360; i+= 30) {
    sx = cos((i-90)*0.0174532925);
    sy = sin((i-90)*0.0174532925);
    x0 = sx*57+64;
    yy0 = sy*57+64;
    x1 = sx*50+64;
    yy1 = sy*50+64;

    tft.drawLine(x0, yy0, x1, yy1, TFT_BLUE);
  }

  // Draw 60 dots
  for(int i = 0; i<360; i+= 6) {
    sx = cos((i-90)*0.0174532925);
    sy = sin((i-90)*0.0174532925);
    x0 = sx*53+64;
    yy0 = sy*53+64;
    
    tft.drawPixel(x0, yy0, TFT_BLUE);
    if(i==0 || i==180) tft.fillCircle(x0, yy0, 1, TFT_CYAN);
    if(i==0 || i==180) tft.fillCircle(x0+1, yy0, 1, TFT_CYAN);
    if(i==90 || i==270) tft.fillCircle(x0, yy0, 1, TFT_CYAN);
    if(i==90 || i==270) tft.fillCircle(x0+1, yy0, 1, TFT_CYAN);
  }

  tft.fillCircle(65, 65, 3, TFT_RED);

  // Draw text at position 64,125 using fonts 4
  // Only font numbers 2,4,6,7 are valid. Font 6 only contains characters [space] 0 1 2 3 4 5 6 7 8 9 : . a p m
  // Font 7 is a 7 segment font and only contains characters [space] 0 1 2 3 4 5 6 7 8 9 : .
  tft.drawCentreString("Time flies",64,130,4);

  targetTime = millis() + 1000; 
}

void loop() {
  if (targetTime < millis()) {
    targetTime = millis()+1000;
    ss++;              // Advance second
    if (ss==60) {
      ss=0;
      mm++;            // Advance minute
      if(mm>59) {
        mm=0;
        hh++;          // Advance hour
        if (hh>23) {
          hh=0;
        }
      }
    }

    // Pre-compute hand degrees, x & y coords for a fast screen update
    sdeg = ss*6;                  // 0-59 -> 0-354
    mdeg = mm*6+sdeg*0.01666667;  // 0-59 -> 0-360 - includes seconds
    hdeg = hh*30+mdeg*0.0833333;  // 0-11 -> 0-360 - includes minutes and seconds
    hx = cos((hdeg-90)*0.0174532925);    
    hy = sin((hdeg-90)*0.0174532925);
    mx = cos((mdeg-90)*0.0174532925);    
    my = sin((mdeg-90)*0.0174532925);
    sx = cos((sdeg-90)*0.0174532925);    
    sy = sin((sdeg-90)*0.0174532925);

    if (ss==0 || initial) {
      initial = 0;
      // Erase hour and minute hand positions every minute
      tft.drawLine(ohx, ohy, 65, 65, TFT_BLACK);
      ohx = hx*33+65;    
      ohy = hy*33+65;
      tft.drawLine(omx, omy, 65, 65, TFT_BLACK);
      omx = mx*44+65;    
      omy = my*44+65;
    }

      // Redraw new hand positions, hour and minute hands not erased here to avoid flicker
      tft.drawLine(osx, osy, 65, 65, TFT_BLACK);
      tft.drawLine(ohx, ohy, 65, 65, TFT_WHITE);
      tft.drawLine(omx, omy, 65, 65, TFT_WHITE);
      osx = sx*47+65;    
      osy = sy*47+65;
      tft.drawLine(osx, osy, 65, 65, TFT_RED);

    tft.fillCircle(65, 65, 3, TFT_RED);
  }
}



1 Like

@NickCallegari, we need to start over cause I have no idea where you are at. Did you get one of the displays working?

I am working on getting the latest version of TFT_eSPI compiling for the Boron with DeviceOS v6.1.1. If and when I get it working, I'll share the Workbench files with you.

UPDATE: I have the tft_clock.ino example working on a 240x320 ILI9341 display with the newest TFT_eSPI library. I only defined the CS and DC pins and left thre display reset pin unconnected. I can share, on dropbox, the entire Workbench folder that you can copy into your VSCode workspace and go from there. Would that interest you?

1 Like

Hi @peekay123, thanks SO MUCH for continuing to help here, I know this has likely been a bit frustrating (and things got confusing above, I'm also a bit lost haha), so I appreciate it!

I have not gotten the displays working, and would very much be interested in seeing the entire workbench folder via dropbox if you are still okay with that :slight_smile:

1 Like

I should warn you that the ST7735 and ST7739 great once you get them working, but bringing one up is a super huge pain! If you go to Adafruit and get their pre-configured board and library, it works perfectly. If you grab a random board, even a well-known brand like Waveshare, danger lurks.

There are several versions of the ST7735 and ST7739 with different initialization sequences, and each display can have left and top offsets, rotation, and inversion parameters that need to be set properly for that specific display. The libraries are the same but there are a lot of hidden parameters. It's insanity!

However it is completely possible, just a bit of work. And sometimes you'll run across a configuration that just works out of the box.

Also there's nothing wrong with TFT_eSPI, however I use Adafruit_ST7735_RK for ST7735 and ST7739. I just updated it to the latest Adafruit upstream version.

1 Like

@NickCallegari, the TFT_eSPI Workbench project folder is available here:

https://www.dropbox.com/scl/fi/fy0lgke79zuxju3dv5qei/TFTeSPI.zip?rlkey=alp1dtolrewlilsfn62n3fqu9&st=6w39e4vr&dl=0

Some notes:

  • The TFTeSPI.ino applicaton is a copy of the TFT_Clock example with the added Particle bits
  • The User_Setup.h file in the library src folder is configured as follows:
    • GC9A01 display driver
    • TFT_WIDTH = 240, TFT_HEIGHT = 240
    • In new "Particle" section, TFT_CS = D4, TFT_DC = D5 and TFT_RST = -1 (disabled).
    • The hardware SPI MOSI and SCK pins are used by default
  • The DeviceOS is set for v6.1.1 with a Boron target

Give that a shot and let me know how it goes!

1 Like

Hi @peekay123, thanks so much for working through this and sending the files over, after a little initial troubleshooting (ends up one of the 1.28 inch LCDs was not working correctly out of the box) I was able to get the LCD to work!

Interesting how the library you shared via dropbox had significantly more drivers than the one I downloaded via workbench, very useful!

And thanks for the notes on the ST7735 and ST7739 drivers, @rickkas7, good to know!

For the project I am working on, I have an Arduino Mega communicating with some particle Borons, and ends up the 1.9 inch LCD above should be connected to the Arduino for what I'm working on (so I just needed to get the GC9A01 driver LCD working for the Boron). Additionally, there seems to be more support for the ST7798 driver on particle via the updated library @rickkas7 mentioned above (for those that need help with that reading this forum).

I should be able to tweak this file from here, I'll reach out if I run into significant issues moving forward (after googling and scouring forums, if more problems arise).

And, a huge thank you again to @peekay123 for helping resolve this, I deeply appreciate it :slight_smile:

3 Likes

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