>1 Round GC9A01 displays using 3 Gauges and ESP32doit-devkit-v1

Dear friends and community,

This particular project started out as an extension from my earlier previous post regarding Round GC9A01 displays. I began by learning how to create my own images (instrument needles) and building sprites then implementing them in code on a background image. Sure enough I got bogged down in ‘flickering’ displays before I really understood how to use sprites correctly. I then searched for how to add more displays and found out TFT_eSPI wasn’t really written from the start for multiple displays, Bodmer himself stated as much. However I did find some help from ‘The Last Output Workshop’ but it didn’t really give me what I wanted so I set about building my own equivalent.

I have success in using three GC9A01 displays on one ESP32 each display being ‘Coolant Pressure’, ‘Coolant Temp’ and ‘Oil Pressure’ respectively. This is done however with one drawback, I use three functions to control each of the displays.

void loop() {

  plotGauge1(); // COOLANT PRESSURE

  plotGauge2(); // COOLANT TEMPERATURE

  plotGauge3(); // OIL PRESSURE 

}

Whilst this works (Happy to post the full code for anyone to use) I wanted to control all three gauges more or less ‘simultaneously’ and this is where alas I’ve got problems. I cannot understand why the ESP32 does not control the associated gauge display pins from the software sequentially for some reason the displays and the String(scalex) values get jumbled up?

config.h

#define TFT1_CS 21          // TFT 1 chip select pin (set to -1 to use TFT_eSPI setup)

#define TFT2_CS 19          // TFT 2 chip select pin (set to -1 to use TFT_eSPI setup)

#define TFT3_CS 2

In the first instance as a ‘Newbie’ I’m looking for an explanation I can learn from and try to solve myself, Secondly some guidance on where my understanding is failing because I’m really not understanding the sequential nature of the code?

Here is my full annotated code if someone would kindly peruse through and have a’giggle’ if it’s blatantly obvious! I’ve left all my ‘trial and error’ code in place to emphasis the issue.

INCREASE the needle value works, DECREASE the needle value does not and I can’t see why?

Thank you in advance for any help or comments.

#include <Arduino.h>
#include <SPI.h>
// Gauge Background Images
#include <gauge1.h> ////Coolant Pressure
#include <gauge2.h> ////Coolant Temperature
#include <gauge3.h> ////Oil Pressure
// Needle Options
#include <InstNeedle.h> ////0xAD55 adjusted
// Font type
#include <Free_Fonts.h>
// Include the graphics library (this includes the sprite functions)
#include <TFT_eSPI.h>
#include <config.h>
// Width and height of background sprite
#define sprite_WIDTH  239
#define sprite_HEIGHT 239


// Declare object "tft"
TFT_eSPI tft = TFT_eSPI();
// Declare Sprite object "spr" with pointer to "tft" object
// Declare Sprites for background and needle
TFT_eSprite gaugeBack = TFT_eSprite(&tft);
TFT_eSprite needle = TFT_eSprite(&tft);


uint16_t* tft1_buffer;
uint16_t* tft2_buffer;
uint16_t* tft3_buffer;
bool      buffer_loaded = false;
uint16_t  spr_width = 0;
uint16_t  bg_color =0;


/*********************************************
*      LED's CURRENTLY NOT USED IN CODE
* Set the frequency of LEDC channel 0 to 5000
* and the rsolution to 8 bits, that is,
* the duty cycle can be selected from 0 to 255.
**********************************************/
const int pwmRLedChannelTFT = 0;
const int pwmGLedChannelTFT = 1;
const int pwmYLedChannelTFT = 2; 
const int pwmFreq = 5000;
const int pwmResolution_bits = 8;
int RedLED = 5;
int GreenLED = 17;
int YellowLED = 2; //// Will have to change this pin no.


// Adjust this value based on the number of displays
const int NUM_GAUGES = 3; // Number of gauges to display 
// Create an Array to store the ESP32 CS pins, each display must have a dedicated pin
// simply add more pins to the Array for more displays
////const int CS_PINS[NUM_GAUGES] = { TFT1_CS};
////const int CS_PINS[NUM_GAUGES] = { TFT1_CS, TFT2_CS};
const int CS_PINS[NUM_GAUGES] = { TFT1_CS, TFT2_CS, TFT3_CS };
////int currentScreenIndex = 0; //// Not sure what this code does as currentScreenIndex isn't used?


void createNeedle(){
  ////Now create the 'size' of the needle for me created in Paint 9x100px
  ////pushImage and setPivot depend on size of image created in Paint
  needle.createSprite(9,90);
  needle.pushImage(0,0,9,90,BlueNeedle);
  needle.setPivot(4,84); // RedPointerSmall 
  needle.pushRotated(&gaugeBack, 50, TFT_TRANSPARENT);  
}


void createBackground(){ 
  //Set pivot point relative to sprite           
  gaugeBack.setPivot(120,120);  
  gaugeBack.pushSprite(0, 0);
}


void plotGauge(){
  //Push the needle onto the background image gaugeBack
  //with colours that are transparent
  //Simulated needle or pointer sweeping around gauge  


  /************************************/
  /* INCREASE the needle value        */
  /* MUST MATCH the Gauge scale range */
  /********************************** */
  digitalWrite(CS_PINS[0], LOW);
  for (int i=0; i<271; i+=1){ 
    int result = map (i,0,270,-135,135);
    // Original tutorial code
    // Push the needle sprite onto the gauge background
    needle.pushRotated(&gaugeBack, result, TFT_TRANSPARENT);
    
     ////if(i>0 && i<36){  
      ////gaugeBack.setTextColor(TFT_YELLOW,0xAD55);
    
     ///////////////////////////////////////////////////////
     //                                                   //
     // Note for TEST purposes the scaling MUST match and //
     // map to the selected Gauge scale range             //
     //                                                   //
     ///////////////////////////////////////////////////////
     // Convert the scale range for example Gauge 3 which is 
     // Oil Pressure into the gauge scale 0 to 60
      ////int scale1 = map (i,0,270,0,40); //COOLANT PRESSURE
      ////int scale2 = map (i,0,270,20,100); //COOLANT TEMPERATURE
      ////int scale3 = map (i,0,270,0,60); //OIL PRESSURE
      /*
      digitalWrite(CS_PINS[0], LOW);
      gaugeBack.drawString(String(scale1),118,184,GFXFF); //// <---THIS IS IMPORTANT 
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge1); //COOLANT PRESSURE
      digitalWrite(CS_PINS[0], HIGH);
      delay(2);
      */
     ////}
    
      ////digitalWrite(CS_PINS[1], LOW);
      ////gaugeBack.drawString(String(scale2),118,184,GFXFF); //// <---THIS IS IMPORTANT   
      ////gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      ////gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge2); //COOLANT TEMPERATURE
      ////digitalWrite(CS_PINS[1], HIGH);
      ////delay(2);
    
      ////digitalWrite(CS_PINS[2], LOW);
      ////gaugeBack.drawString(String(scale3),118,184,GFXFF); //// <---THIS IS IMPORTANT 
      ////gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      ////gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge3); //OIL PRESSURE
      ////digitalWrite(CS_PINS[2], HIGH);
    
    
    if(i>0 && i<36){
      gaugeBack.setTextColor(TFT_YELLOW,0xAD55);
      int scale1 = map (i,0,270,0,40); //// <---THIS IS IMPORTANT
      gaugeBack.drawString(String(scale1),118,184,GFXFF);  
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge1); //COOLANT PRESSURE
      delay(2);
    }


    /*  TESTING CODE ONLY
    if(i>0 && i<36){  
      gaugeBack.setTextColor(TFT_YELLOW,0xAD55);
      int scale2 = map (i,0,270,20,100); //// <---THIS IS IMPORTANT
      digitalWrite(CS_PINS[1], LOW);
      gaugeBack.drawString(String(scale2),118,184,GFXFF);    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge2); //COOLANT TEMPERATURE
      digitalWrite(CS_PINS[1], HIGH);
      delay(2);
    }
    */    
    /* TESTING CODE ONLY
    if(i>0 && i<36){  
      gaugeBack.setTextColor(TFT_YELLOW,0xAD55);
      int scale3 = map (i,0,270,0,60); //// <---THIS IS IMPORTANT
      digitalWrite(CS_PINS[2], LOW);
      gaugeBack.drawString(String(scale3),118,184,GFXFF);    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge3); //OIL PRESSURE
      digitalWrite(CS_PINS[2], HIGH);
      delay(2);
    }
    */


    if (i>36 && i<236){      
      gaugeBack.setTextColor(TFT_GREEN,0xAD55);
      int scale1 = map (i,0,270,0,40); //// <---THIS IS IMPORTANT
      gaugeBack.drawString(String(scale1),118,184,GFXFF);  
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge1); //COOLANT PRESSURE      


      /*  TESTING CODE ONLY
      gaugeBack.setTextColor(TFT_YELLOW,0xAD55);
      int scale2 = map (i,0,270,20,100); //// <---THIS IS IMPORTANT
      digitalWrite(CS_PINS[1], LOW);
      gaugeBack.drawString(String(scale2),118,184,GFXFF);    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge2); //COOLANT TEMPERATURE
      digitalWrite(CS_PINS[1], HIGH);
      delay(2);
      */


      /*  TESTING CODE ONLY
      int scale3 = map (i,0,270,0,60); //// <---THIS IS IMPORTANT
      digitalWrite(CS_PINS[2], LOW); 
      gaugeBack.drawString(String(scale3),118,184,GFXFF);      
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge3); //OIL PRESSURE
      digitalWrite(CS_PINS[2], HIGH);   
      }
      */
    }      
    if (i>236){      
      gaugeBack.setTextColor(TFT_RED,0xAD55);
      int scale1 = map (i,0,270,0,40); //// <---THIS IS IMPORTANT
      gaugeBack.drawString(String(scale1),118,184,GFXFF);  
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge1); //COOLANT PRESSURE      


      /*
      gaugeBack.setTextColor(TFT_YELLOW,0xAD55);
      int scale2 = map (i,0,270,20,100); //// <---THIS IS IMPORTANT
      digitalWrite(CS_PINS[1], LOW); 
      gaugeBack.drawString(String(scale2),118,184,GFXFF);    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge2); //COOLANT TEMPERATURE
      digitalWrite(CS_PINS[1], HIGH);
      delay(2);
      */
      /*
      gaugeBack.setTextColor(TFT_RED,0xAD55);
      int scale3 = map (i,0,270,0,60); //// <---THIS IS IMPORTANT
      digitalWrite(CS_PINS[2], LOW);      
      gaugeBack.drawString(String(scale3),118,184,GFXFF);     
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge3); //OIL PRESSURE
      digitalWrite(CS_PINS[2], HIGH);
      */    
    }
  } digitalWrite(CS_PINS[0], HIGH);


  digitalWrite(CS_PINS[1], LOW);
  for (int i=0; i<271; i+=1){
    int result = map (i,0,270,-135,135);
    needle.pushRotated(&gaugeBack, result, TFT_TRANSPARENT);
    if(i>0 && i<36){  
      gaugeBack.setTextColor(TFT_YELLOW,0xAD55);
      int scale2 = map (i,0,270,20,100); //// <---THIS IS IMPORTANT
      gaugeBack.drawString(String(scale2),118,184,GFXFF);    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge2); //COOLANT TEMPERATURE
      delay(2);
    }
    if (i>36 && i<236){
      gaugeBack.setTextColor(TFT_GREEN,0xAD55);
      int scale2 = map (i,0,270,20,100); //// <---THIS IS IMPORTANT
      gaugeBack.drawString(String(scale2),118,184,GFXFF);    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge2); //COOLANT TEMPERATURE
      delay(2);
    }
    if (i>236){
      gaugeBack.setTextColor(TFT_RED,0xAD55);
      int scale2 = map (i,0,270,20,100); //// <---THIS IS IMPORTANT
      gaugeBack.drawString(String(scale2),118,184,GFXFF);    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge2); //COOLANT TEMPERATURE
      delay(2);
    }
  } digitalWrite(CS_PINS[1], HIGH);


  digitalWrite(CS_PINS[2], LOW);
  for (int i=0; i<271; i+=1){
    int result = map (i,0,270,-135,135);
    needle.pushRotated(&gaugeBack, result, TFT_TRANSPARENT);
    if(i>0 && i<36){  
      gaugeBack.setTextColor(TFT_YELLOW,0xAD55);
      int scale3 = map (i,0,270,0,60); //// <---THIS IS IMPORTANT
      gaugeBack.drawString(String(scale3),118,184,GFXFF);    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge3); //OIL PRESSURE
      delay(2);
    }
    if (i>36 && i<236){
      gaugeBack.setTextColor(TFT_GREEN,0xAD55);
      int scale3 = map (i,0,270,0,60); //// <---THIS IS IMPORTANT
      gaugeBack.drawString(String(scale3),118,184,GFXFF);    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge3); //OIL PRESSURE
      delay(2);
    }
    if (i>236){
      gaugeBack.setTextColor(TFT_RED,0xAD55);
      int scale3 = map (i,0,270,0,60); //// <---THIS IS IMPORTANT
      gaugeBack.drawString(String(scale3),118,184,GFXFF);   
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge3); //OIL PRESSURE
      delay(2);
    }
  } digitalWrite(CS_PINS[2], HIGH);


  /************************************/
  /* DECREASE the needle value        */
  /* MUST MATCH the Gauge scale range */
  /********************************** */
  
  for (int i=270; i>=0; i-=5){ //Gauge decreasing
    int result = map (i,0,270,-135,135);
    needle.pushRotated(&gaugeBack, result, TFT_TRANSPARENT);
    if(i<270 && i>236){
      gaugeBack.setTextColor(TFT_RED,0xAD55);
      int scale1 = map (i,0,270,0,40);
      int scale2 = map (i,0,270,20,100);
      int scale3 = map (i,0,270,0,60);


      digitalWrite(CS_PINS[0], LOW);
      gaugeBack.drawString(String(scale1),118,184,GFXFF); //// <---THIS IS IMPORTANT     
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge1); //COOLANT PRESSURE
      digitalWrite(CS_PINS[0], HIGH);
      delay(2);
      digitalWrite(CS_PINS[1], LOW);
      gaugeBack.drawString(String(scale2),118,184,GFXFF); //// <---THIS IS IMPORTANT    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge2); //COOLANT TEMPERATURE
      digitalWrite(CS_PINS[1], HIGH);
      delay(2);
      digitalWrite(CS_PINS[2], LOW);
      gaugeBack.drawString(String(scale3),118,184,GFXFF); //// <---THIS IS IMPORTANT     
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge3); //OIL PRESSURE
      digitalWrite(CS_PINS[2], HIGH);    
    
      ////ledcWrite(pwmRLedChannelTFT, 200); //LOW Alarm
      ////ledcWrite(pwmGLedChannelTFT, 0);    
    }
    if (i<236 && i>36){
      gaugeBack.setTextColor(TFT_GREEN,0xAD55);   
      int scale1 = map (i,0,270,0,40);
      int scale2 = map (i,0,270,20,100);
      int scale3 = map (i,0,270,0,60);


      digitalWrite(CS_PINS[0], LOW);
      gaugeBack.drawString(String(scale1),118,184,GFXFF); //// <---THIS IS IMPORTANT     
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge1); //COOLANT PRESSURE
      digitalWrite(CS_PINS[0], HIGH);
      delay(2);
      digitalWrite(CS_PINS[1], LOW);
      gaugeBack.drawString(String(scale2),118,184,GFXFF); //// <---THIS IS IMPORTANT    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge2); //COOLANT TEMPERATURE
      digitalWrite(CS_PINS[1], HIGH);
      delay(2);
      digitalWrite(CS_PINS[2], LOW);
      gaugeBack.drawString(String(scale3),118,184,GFXFF); //// <---THIS IS IMPORTANT    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge3); //OIL PRESSURE
      digitalWrite(CS_PINS[2], HIGH);
    
      ////ledcWrite(pwmGLedChannelTFT, 200); //Cancel HIGH Alarm
      ////ledcWrite(pwmRLedChannelTFT, 0);    
    }
    if (i<36){
      gaugeBack.setTextColor(TFT_YELLOW,0xAD55);    
      int scale1 = map (i,0,270,0,40);
      int scale2 = map (i,0,270,20,100);
      int scale3 = map (i,0,270,0,60);


      digitalWrite(CS_PINS[0], LOW);
      gaugeBack.drawString(String(scale1),118,184,GFXFF); //// <---THIS IS IMPORTANT     
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge1); //COOLANT PRESSURE
      digitalWrite(CS_PINS[0], HIGH);
      delay(2);
      digitalWrite(CS_PINS[1], LOW);
      gaugeBack.drawString(String(scale2),118,184,GFXFF); //// <---THIS IS IMPORTANT    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge2); //COOLANT TEMPERATURE
      digitalWrite(CS_PINS[1], HIGH);
      delay(2);
      digitalWrite(CS_PINS[2], LOW);
      gaugeBack.drawString(String(scale3),118,184,GFXFF); //// <---THIS IS IMPORTANT    
      gaugeBack.pushSprite(0,0,TFT_TRANSPARENT);
      gaugeBack.pushImage(0,0,240,240,(uint16_t *)gauge3); //OIL PRESSURE
      digitalWrite(CS_PINS[2], HIGH);     
    }
  }   
}


void setup() {
  //Serial.begin(115200); //Debug code checking  


  // Initialise the TFT registers
  tft.init();
  //// Fill the Gauge background with a selected colour
  //// and align with the config.h definitions.
  for (int i = 0; i < NUM_GAUGES; i++) {
    pinMode(CS_PINS[i], OUTPUT);
    digitalWrite(CS_PINS[i], LOW);  // Select the display pin
    tft.fillScreen(TFT_LIGHTGREY);
    delay(1000);
    digitalWrite(CS_PINS[i], HIGH);  // Deselect the display pin
  }
  delay(1000);
  //Call the function to create the background image
  createBackground(); //// Open gauge background


  //// THIS Serial.print DOES NOTHING ONLY GENERATES ESP32 WARNINGS
  //// I've read up on these and my understanding is these
  //// are ESP32 firmware issues?
  /*
  Serial.print("TFT1_CS pin value: ");
  Serial.print(" ");
  Serial.print(CS_PINS[0]);
  Serial.print("TFT2_CS pin value: ");
  Serial.print(" ");
  Serial.print(CS_PINS[1]);
  Serial.print("TFT3_CS pin value: ");
  Serial.print(" ");
  Serial.println(CS_PINS[2]);
  /*
  ts Ju[    15][E][esp32-hal-gpio.c:102] __pinMode(): Invalid pin selected
  E (16) gpio: gpio_set_level(227): GPIO output gpio_num error
  [    23][E][esp32-hal-gpio.c:102] __pinMode(): Invalid pin selected
  E (30) gpio: gpio_set_level(227): GPIO output gpio_num error
  */  


  //// The byte order can be swapped (set true for TFT_eSPI)
  tft.setSwapBytes(true); // We need to swap the colour bytes (endianess)
  //// For the images, depends on how/type of image is converted.
  //// Using image2cpp(to generate images) you don't have the Little or Big Endian byte order choice.
  gaugeBack.setSwapBytes(true);
  needle.setSwapBytes(true);


  /* Declare Sprites for background and needle */
  ////Now create the sprites
  gaugeBack.createSprite(sprite_WIDTH, sprite_HEIGHT);
  gaugeBack.fillSprite(TFT_TRANSPARENT); //Used if overlaid on something else
  gaugeBack.setTextDatum(4);
  gaugeBack.setFreeFont(&FreeSerifItalic12pt7b);
  gaugeBack.setTextColor(TFT_BLACK,0xAD55);  
  //needle.createSprite(9, 100); //Depends on needle image you create
  needle.createSprite(9, 90); 


  ////Create the needle first
  createNeedle();


  /* CODE CURRENTLY NOT USED FOR LED's
  //Sets up a PWM channel, frequency and resolution.
  ledcSetup(pwmGLedChannelTFT, pwmFreq, pwmResolution_bits);
  //Binds the LEDC channel to a specified IO port for OUTPUT.
  ledcAttachPin(RedLED, pwmRLedChannelTFT); //Set LEDC channel 0 to output on IO5.
  ledcAttachPin(GreenLED, pwmGLedChannelTFT); //Set LEDC channel 0 to output on IO5.
  ledcAttachPin(YellowLED, pwmYLedChannelTFT); //Set LEDC channel 0 to output on IO5.
  //Writes a duty cycle percentage to a channel.
  ledcWrite(pwmGLedChannelTFT, 90);
  */


}


void loop() {
  plotGauge();
}