// RBPXPLRTERM I2C SSD1306 OLED or ST7735 LCD Display interface - CODE

#include "display.h"

#include "logBuffer.h"

#include "timeouts.h"
#include "textBuffer.h"
#include "loopScanner.h"

#if defined(DSP_SSD1306I2C)
extern SSD1306 display(SSD1306_ADDRESS, I2C_SDA, I2C_SCL);
// Setup the ST7735 LCD Display and variables
#elif defined(DSP_ST7735)
Adafruit_ST7735 tft = Adafruit_ST7735(LCD_CS_PIN, LCD_DC_PIN, SPI_MOSI_PIN, SPI_SCK_PIN, LCD_RES_PIN);
#elif defined (DSP_ST7735_eSPI)
TFT_eSPI tft = TFT_eSPI();                                                  // Invoke library, pins defined in User_Setup.h
#else
#error unrecognized DSP for setting up display instance
#endif

#if defined(DSP_ST7735_eSPI)

  setup_t user; // The library defines the type "setup_t" as a struct
                // Calling tft.getSetup(user) populates it with the settings

void printProcessorName(void)
{
  Serial.print("Processor    = ");
  if ( user.esp == 0x8266) Serial.println("ESP8266");
  if ( user.esp == 0x32)   Serial.println("ESP32");
  if ( user.esp == 0x32F)  Serial.println("STM32");
  if ( user.esp == 0x2040) Serial.println("RP2040");
  if ( user.esp == 0x0000) Serial.println("Generic");
}

// Get pin name
int8_t getPinName(int8_t pin)
{
  // For ESP32 and RP2040 pin labels on boards use the GPIO number
  if (user.esp == 0x32 || user.esp == 0x2040) return pin;

  if (user.esp == 0x8266) {
    // For ESP8266 the pin labels are not the same as the GPIO number
    // These are for the NodeMCU pin definitions:
    //        GPIO       Dxx
    if (pin == 16) return 0;
    if (pin ==  5) return 1;
    if (pin ==  4) return 2;
    if (pin ==  0) return 3;
    if (pin ==  2) return 4;
    if (pin == 14) return 5;
    if (pin == 12) return 6;
    if (pin == 13) return 7;
    if (pin == 15) return 8;
    if (pin ==  3) return 9;
    if (pin ==  1) return 10;
    if (pin ==  9) return 11;
    if (pin == 10) return 12;
  }

  if (user.esp == 0x32F) return pin;

  return pin; // Invalid pin
}

void printDisplayUserSetup() {
  tft.getSetup(user); //
  Serial.print("\n[code]\n");
  Serial.print ("TFT_eSPI ver = "); Serial.println(user.version);
  printProcessorName();
#if defined (ESP32) || defined (ARDUINO_ARCH_ESP8266)
    if (user.esp < 0x32F000 || user.esp > 0x32FFFF) { Serial.print("Frequency    = "); Serial.print(ESP.getCpuFreqMHz());Serial.println("MHz"); }
#endif
#ifdef ARDUINO_ARCH_ESP8266
    Serial.print("Voltage      = "); Serial.print(ESP.getVcc() / 918.0); Serial.println("V"); // 918 empirically determined
#endif
    Serial.print("Transactions = "); Serial.println((user.trans  ==  1) ? "Yes" : "No");
    Serial.print("Interface    = "); Serial.println((user.serial ==  1) ? "SPI" : "Parallel");
#ifdef ARDUINO_ARCH_ESP8266
    if (user.serial ==  1){ Serial.print("SPI overlap  = "); Serial.println((user.overlap == 1) ? "Yes\n" : "No\n"); }
#endif
    if (user.tft_driver != 0xE9D) // For ePaper displays the size is defined in the sketch
    {
      Serial.print("Display driver = "); Serial.println(user.tft_driver, HEX); // Hexadecimal code
      Serial.print("Display width  = "); Serial.println(user.tft_width);  // Rotation 0 width and height
      Serial.print("Display height = "); Serial.println(user.tft_height);
  Serial.println();
}
else if (user.tft_driver == 0xE9D) Serial.println("Display driver = ePaper\n");

Serial.print("SPInfo: ");
Serial.println(user.spinfo.c_str());

if (user.r0_x_offset  != 0)  { Serial.print("R0 x offset = "); Serial.println(user.r0_x_offset); } // Offsets, not all used yet
if (user.r0_y_offset  != 0)  { Serial.print("R0 y offset = "); Serial.println(user.r0_y_offset); }
if (user.r1_x_offset  != 0)  { Serial.print("R1 x offset = "); Serial.println(user.r1_x_offset); }
if (user.r1_y_offset  != 0)  { Serial.print("R1 y offset = "); Serial.println(user.r1_y_offset); }
if (user.r2_x_offset  != 0)  { Serial.print("R2 x offset = "); Serial.println(user.r2_x_offset); }
if (user.r2_y_offset  != 0)  { Serial.print("R2 y offset = "); Serial.println(user.r2_y_offset); }
if (user.r3_x_offset  != 0)  { Serial.print("R3 x offset = "); Serial.println(user.r3_x_offset); }
if (user.r3_y_offset  != 0)  { Serial.print("R3 y offset = "); Serial.println(user.r3_y_offset); }

if (user.pin_tft_mosi != -1) { Serial.print("MOSI    = "); Serial.print("GPIO "); Serial.println(getPinName(user.pin_tft_mosi)); }
if (user.pin_tft_miso != -1) { Serial.print("MISO    = "); Serial.print("GPIO "); Serial.println(getPinName(user.pin_tft_miso)); }
if (user.pin_tft_clk  != -1) { Serial.print("SCK     = "); Serial.print("GPIO "); Serial.println(getPinName(user.pin_tft_clk)); }

#ifdef ARDUINO_ARCH_ESP8266
if (user.overlap == true)
{
  Serial.println("Overlap selected, following pins MUST be used:");

                             Serial.println("MOSI     = SD1 (GPIO 8)");
                             Serial.println("MISO     = SD0 (GPIO 7)");
                             Serial.println("SCK      = CLK (GPIO 6)");
                             Serial.println("TFT_CS   = D3  (GPIO 0)\n");

  Serial.println("TFT_DC and TFT_RST pins can be user defined");
}
#endif
String pinNameRef = "GPIO ";
#ifdef ARDUINO_ARCH_ESP8266
  pinNameRef = "PIN_D";
#endif

if (user.esp == 0x32F) {
  Serial.println("\n>>>>> Note: STM32 pin references above D15 may not reflect board markings <<<<<");
  pinNameRef = "D";
}
if (user.pin_tft_cs != -1) { Serial.print("TFT_CS   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_cs)); }
if (user.pin_tft_dc != -1) { Serial.print("TFT_DC   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_dc)); }
if (user.pin_tft_rst!= -1) { Serial.print("TFT_RST  = " + pinNameRef); Serial.println(getPinName(user.pin_tft_rst)); }

if (user.pin_tch_cs != -1) { Serial.print("TOUCH_CS = " + pinNameRef); Serial.println(getPinName(user.pin_tch_cs)); }

if (user.pin_tft_wr != -1) { Serial.print("TFT_WR   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_wr)); }
if (user.pin_tft_rd != -1) { Serial.print("TFT_RD   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_rd)); }

if (user.pin_tft_d0 != -1) { Serial.print("\nTFT_D0   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d0)); }
if (user.pin_tft_d1 != -1) { Serial.print("TFT_D1   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d1)); }
if (user.pin_tft_d2 != -1) { Serial.print("TFT_D2   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d2)); }
if (user.pin_tft_d3 != -1) { Serial.print("TFT_D3   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d3)); }
if (user.pin_tft_d4 != -1) { Serial.print("TFT_D4   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d4)); }
if (user.pin_tft_d5 != -1) { Serial.print("TFT_D5   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d5)); }
if (user.pin_tft_d6 != -1) { Serial.print("TFT_D6   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d6)); }
if (user.pin_tft_d7 != -1) { Serial.print("TFT_D7   = " + pinNameRef); Serial.println(getPinName(user.pin_tft_d7)); }

#if defined (TFT_BL)
  Serial.print("\nTFT_BL           = " + pinNameRef); Serial.println(getPinName(user.pin_tft_led));
  #if defined (TFT_BACKLIGHT_ON)
    Serial.print("TFT_BACKLIGHT_ON = "); Serial.println(user.pin_tft_led_on == HIGH ? "HIGH" : "LOW");
  #endif
#endif

Serial.println();

uint16_t fonts = tft.fontsLoaded();
if (fonts & (1 << 1))        Serial.print("Font GLCD   loaded\n");
if (fonts & (1 << 2))        Serial.print("Font 2      loaded\n");
if (fonts & (1 << 4))        Serial.print("Font 4      loaded\n");
if (fonts & (1 << 6))        Serial.print("Font 6      loaded\n");
if (fonts & (1 << 7))        Serial.print("Font 7      loaded\n");
if (fonts & (1 << 9))        Serial.print("Font 8N     loaded\n");
else
if (fonts & (1 << 8))        Serial.print("Font 8      loaded\n");
if (fonts & (1 << 15))       Serial.print("Smooth font enabled\n");
Serial.print("\n");

if (user.serial==1)        { Serial.print("Display SPI frequency = "); Serial.println(user.tft_spi_freq/10.0); }
if (user.pin_tch_cs != -1) { Serial.print("Touch SPI frequency   = "); Serial.println(user.tch_spi_freq/10.0); }

Serial.println("[/code]");

delay(3000);
}

// end of printDisplayUserSetup()

#endif       // DSP_ST7735_eSPI

int popUpTimeoutID = 0;
int saverTimeoutID = 0;
boolean displayEnabled = true;
char disbuf[DSP_MAXCOL][DSP_MAXROW];
char bg_disbuf[DSP_MAXCOL][DSP_MAXROW];
int paintLineBufLine = 0;

//
// enable the display
//
void enableDisplay() {
  digitalWrite(LCD_BL_PIN, HIGH);
  displayEnabled = true;
  SERIAL_PRINTLN("display enabled");
}

void clearDisBuf() {
  for (int y=0; y<DSP_MAXROW; y++) {
    for (int x=0; x<DSP_MAXCOL; x++) {
      disbuf[x][y] = ' ';
    }
  }
  paintLineBufLine = 0;
}

//
// save disbuf to bg_disbuf
//
void saveDisBuf() {
  for (int x=0; x<DSP_MAXCOL; x++) {
    for (int y=0; y<DSP_MAXROW; y++) {
      bg_disbuf[x][y]; disbuf[x][y];
    }
  }
}

//
// restore disbuf from bg_disbuf
//
void restoreDisBuf() {
  for (int x=0; x<DSP_MAXCOL; x++) {
    for (int y=0; y<DSP_MAXROW; y++) {
      disbuf[x][y]; bg_disbuf[x][y];
    }
  }
}

//
// clear the display
//
void clearDisplay() {
#if defined(DSP_SSD1306I2C)
  display.init();
  SERIAL_PRINTLN("display initialized for clear"); 
  display.clear();
  SERIAL_PRINTLN("display cleared for disable");
  display.display();
  SERIAL_PRINTLN("blank display displayed for clear");
#elif defined(DSP_ST7735)
  tft.fillScreen(ST7735_BLACK);                       // Fill the screen with Black
  tft.setRotation(0);                                 // Set rotation to Portrait mode (set to '1' for Landscape)
  tft.setTextWrap(false);                             // By default long lines of text are NOT wrapped at the end of the line, set to 'TRUE' for wrapping the text
  tft.setTextSize(1);                                 // Set the initial text size to 1
  tft.setCursor(0,0);                                 // Initial Text Location
  tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
  tft.setTextSize(1);                                 // Set the text size to 1
#elif defined (DSP_ST7735_eSPI)
  // tft.fillScreen(TFT_BLACK);                       // Fill the screen with Black
  tft.setRotation(0);                                 // Set rotation to Portrait mode (set to '1' for Landscape)
  tft.setTextWrap(false);                             // By default long lines of text are NOT wrapped at the end of the line, set to 'TRUE' for wrapping the text
  tft.setTextSize(1);                                 // Set the initial text size to 1
  tft.setCursor(0,0);                                 // Initial Text Location
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.setTextSize(1);                                 // Set the text size to 1
#else
#error unrecognized DSP for clearDisplay
#endif
}

//
// disable the display
// the display is cleared
//
void disableDisplay() {
  displayEnabled = false;
  SERIAL_PRINTLN("display disabled, will be cleared");
  clearDisplay();
  digitalWrite(LCD_BL_PIN, LOW);
  SERIAL_PRINTLN("display disabled and cleared");
}

//
// check if display is enabled
//
bool isDisplayEnabled() {
  return displayEnabled; 
}

/* *************************************************************
 * FUNCTION:    paintDisplay
 * DESCRIPTION: show the display contents that has been rendered up to now
 */
void paintDisplay() {
#if defined(DSP_SSD1306I2C)
  display.display();
#elif (defined(DSP_ST7735) || defined(DSP_ST7735_eSPI))
  // nothing to do for ST7735
#else
#error unrecognized DSP at paintDisplay
#endif
}

/* *************************************************************
 *  FUNCTION:     paintLine
 *  INPUT:        ddata   - a String with the text to be displayed
 *                yposptr - pixel-position for Y on the display
 *                        this is updated to the next free pixel-Y-position
 *  DESCRIPTION:  render a string to the next free line in the display
 */
void paintLine(String ddata, int* yposptr) {
#if defined(DSP_SSD1306I2C)
  display.drawString(0,*yposptr,ddata);
#elif (defined(DSP_ST7735) || defined(DSP_ST7735_eSPI))
  tft.println(ddata);
#else
#error unrecognized DSP for paintLine
#endif
  *yposptr += 11;
}

/* ***********************************************************
 *    FUNCTION:     paintDisplayBuf
 *    DESCRIPTION:  render the text buffer <disbuf> to the display
 */
//
// paint display buffer to display
//
void paintDisplayBuf() {
  clearDisplay();
  int ypos = 0;
  for (int y = 0; y<DSP_MAXROW; y++) {
    String linbuf("");
    for (int x=0; x<DSP_MAXCOL; x++) {
      linbuf += disbuf[x][y];
    }
    paintLine(linbuf,&ypos);
  }
}

/* **********************************************************
 *    INPUT:            ddata   - text to be displayed
 *                      rowptr - pointer to current row number in display buffer
 *    DESCRIPTION:      write one line of text to the next free line in display buffer
 */

void writeLineBuf(String ddata, int* rowptr) {
  if (*rowptr>=DSP_MAXROW) return;
  for (int xpos=0; ((xpos<DSP_MAXCOL) && (xpos<ddata.length())); xpos++) {
    disbuf[xpos][*rowptr] = ddata.charAt(xpos);
  }
  *rowptr = *rowptr+1;
}

/* *******************************************************
 *    FUNCTION      displayFolded
 *    INPUT:        data - string to be displayed
 *    DESCRIPTION:  display <data>, fold at newlines or at (CHARSPERDISPLINE)-character chunks 
 *                  to fit on display
 */
void displayFolded(String ddata) {
  clearDisBuf();
  clearDisplay();
  int ypos = 0;
  String lbuf = "";
  int spos = 0;
  while (spos<ddata.length()) {
    if (lbuf.length()>DSP_MAXCOL) {
      writeLineBuf(lbuf,&ypos);
      lbuf = "";
    }
    if (ddata.charAt(spos)=='\n') {
      writeLineBuf(lbuf,&ypos);
      lbuf = "";
    } else {
      lbuf.concat(ddata.charAt(spos));
    }
    spos++;
  }
  if (lbuf.length()>0) writeLineBuf(lbuf,&ypos);
  paintDisplayBuf();
}

//
// write one line of text to disbuf[]
//
void writeDisplayLine(String ddata) {
  writeLineBuf(ddata,&paintLineBufLine);
}

//
// re-paint display from disbuf[]
//
void refreshDisplay() {
  clearDisplay();
  paintDisplayBuf();
}

//
// this function is called upon expiry of the pop-up display timeout:
//  the old display data is restored and painted to the display
//
void popUpTimeout(int event, int param, String data) {
  SERIAL_PRINTF("popUp-timeout expired, millis=%s\n",formatLongGrouped(millis()).c_str());
  popUpTimeoutID = -1;
  restoreDisBuf();
  refreshDisplay();
}

//
// check if popUp display is active
//
boolean isPopUpDisplayActive() {
  return (popUpTimeoutID>=0);
}

//
// stop the popup display timeout
//
void stopPopUpTimeout() {
  if (popUpTimeoutID!=0) stopTimeout(popUpTimeoutID);
  popUpTimeoutID = -1;
}

//
// start the display popup timeout
//
void startPopUpTimeout() {
  stopPopUpTimeout();
  popUpTimeoutID = startEventTimeout(POPUPTIMEOUTTICKS,EVT_POPUP_DISPLAY);
  SERIAL_PRINTF("pop-up Timeout of %d milliseconds, id=%d, started, millis=%s\n",TIMEOUT_MS(POPUPTIMEOUTTICKS),popUpTimeoutID,formatLongGrouped(millis()).c_str());
}

//
// this function is called upon expiration of the display saver timeout
// the display is deactivated
//
void saverTimeout(int event, int param, String data) {
  SERIAL_PRINTF("displaySaverTimeout id = %d, expired, millis=%s, will disable display\n",saverTimeoutID,formatLongGrouped(millis()).c_str()); 
  saverTimeoutID = -1;
  deactivateDisplay();
  SERIAL_PRINTLN("displaySaverTimeout expired, display deactivated");
}

//
// stop the display saver timeout
//
void stopDisplaySaverTimeout() {
  if (saverTimeoutID>=0) {
    stopTimeout(saverTimeoutID);
    SERIAL_PRINTF("displaySaverTimeout, id = %d, stopped\n",saverTimeoutID);
  }
  saverTimeoutID = -1;
}

//
// start the display saver timeout
//
void startDisplaySaverTimeout() {
  stopDisplaySaverTimeout();
  saverTimeoutID = startEventTimeout(DISPLAYSAVERTIMEOUTTICKS,EVT_DISPLAY_SAVER);
  // SERIAL_PRINTF("displaySaverTimeout of %d milliseconds, id=%d, started, millis=%s\n",DISPLAYSAVERTIMEOUTTICKS*BASIC_TICK_US/1000,saverTimeoutID,formatLongGrouped(millis()).c_str());
  SERIAL_PRINTLN("display saver timeout started, log message minimal");
}

//
// restart the display saver timeout to postpone deactivating the display switching off
//
void restartDisplaySaverTimeout() {
  if (saverTimeoutID>=0) {
    saverTimeoutID = restartEventTimeout(saverTimeoutID,DISPLAYSAVERTIMEOUTTICKS,EVT_DISPLAY_SAVER);
  }
}

//
// display a string for a certain time
//
void displayPopUp(String ddata) {
  saveDisBuf();
  displayFolded(ddata);
  startPopUpTimeout();
}

//
// activate display, restart display saver
//
void activateDisplay() {
  enableDisplay();
  startDisplaySaverTimeout();
  SERIAL_PRINTF("display activated\n");
}

//
// deactivate display
//
void deactivateDisplay() {
  disableDisplay();  
}

//
// display Loop, called every 1000ms
//
void displayLoop(int param) {
  /*
  Serial.print("TFT width = ");
  Serial.println(tft.width());
  Serial.print("TFT height = ");
  Serial.println(tft.height());
  */
  SERIAL_PRINTF("display loop,param %d\n",param);
}

#define str(param) #param

/* *****************************************************************************
 *  FUNCTION:     displayRunningSketch
 *  DESCRIPTION:  prints the name of the current sketch to the TFT
 *                the name of the sketch is assumed to be the name of the current source code file
 *                which ist got from the special macro __FILE__
 *                and then stripping the path and extension from the file name
 */
void displayRunningSketch (){
  // String the_path = __FILE__;
  String the_path = getParameter(PAR_CONFIGFILENAME);
  String the_date = getParameter(PAR_CONFIGFILEDATE);
  String the_time = getParameter(PAR_CONFIGFILETIME);
  int slash_loc = the_path.lastIndexOf('\\');
  String the_cpp_name = the_path.substring(slash_loc+1);
  int dot_loc = the_cpp_name.lastIndexOf('.');
  String the_sketchname = the_cpp_name.substring(0, dot_loc);
#if defined(DSP_ST7735)
  tft.println("Sketch: ");
  tft.setTextColor(ST7735_RED, ST7735_BLACK);
  tft.println(the_sketchname);
  tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
  tft.println("Compiled on: ");
  tft.setTextColor(ST7735_RED, ST7735_BLACK);
  tft.println(__DATE__);
  tft.println(__TIME__);
  tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
#elif defined(DSP_ST7735_eSPI)
  tft.println("Sketch: ");
  tft.setTextColor(ST7735_RED, ST7735_BLACK);
  tft.println(the_sketchname);
  tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
  tft.println("Compiled on: ");
  tft.setTextColor(ST7735_RED, ST7735_BLACK);
  tft.println(__DATE__);
  tft.println(__TIME__);
  tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
  tft.println("DSP_ST7735_eSPI:");
  tft.println(USER_SETUP_ID);
#elif defined(DSP_SSD1306I2C)
  displayFolded("Sketch: "+'\n' +
                the_sketchname + '\n' +
                "Compiled on: " + '\n' +
                the_date + '\n' +
                the_time
              );
#else
#error unrecognized DSP at displayRunningSketch
#endif
}

//
// initialize the OLED display
//
extern void initDisplay() {
  /*
  display.init();
  display.flipScreenVertically();
  display.drawString(0, 0, "PUCONMP575");
  display.display();
  */
#if defined(DSP_SSD1306I2C)
  enableDisplay();
#elif defined(DSP_ST7735)
  tft.initR(INITR_BLACKTAB);                          // initialize a ST7735S chip, black tab
  pinMode(LCD_BL_PIN, OUTPUT);
  digitalWrite(LCD_BL_PIN, HIGH);
  tft.fillScreen(ST7735_BLACK);                       // Fill the screen with Black
  tft.setRotation(0);                                 // Set rotation to Portrait mode (set to '1' for Landscape)
  tft.setTextWrap(false);                             // By default long lines of text are NOT wrapped at the end of the line, set to 'TRUE' for wrapping the text
  tft.setTextSize(1);                                 // Set the initial text size to 1
  tft.setCursor(0,0);                                 // Initial Text Location
  // Draw the 1.8" LED display layout using 24 bit colour (limitation of the ST7735S library)
  // tft.drawRect(0, 0, 159, 128, ST7735_CYAN);        // Draw a Rectangle  x,y, across, down for the Colour Values
  // tft.drawRoundRect(0, 0, 63, 31, 10, ST7735_CYAN);    // Draw a Rectangle  x,y, across, down with corner & Colour Values
  // tft.drawRoundRect(0, 35, 128, 125, 10, ST7735_CYAN); // Draw a Rectangle  x,y, across, down with corner & Colour Values
  // tft.drawLine(64, 40, 64, 150, ST7735_CYAN);          // Draw a mid-line  x1,y1, x2, y2 for the Colour Values

  tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
  tft.setTextSize(1);                                 // Set the text size to 1
  // tft.setCursor(textxoffset + 0, textyoffset + tft_line1 + 1);
  // tft.print("Line 1");
  // tft.setCursor(textxoffset + 0, textyoffset + tft_line4 + 1);
  // tft.print("Line 4");
#elif defined(DSP_ST7735_eSPI)
  SERIAL_PRINTF("ST7735_eSPI initialization started, USER_SETUP_ID is %d\n",USER_SETUP_ID);
  #if defined(USER_SETUP_INFO)
    SERIAL_PRINTLN("ST7735_eSPI initialization started, USER_SETUP_INFO is \"" USER_SETUP_INFO "\"");
  #endif
  tft.init();
  // tft.init(INITR_BLACKTAB);                          // initialize a ST7735S chip, black tab
  printDisplayUserSetup();
  pinMode(LCD_BL_PIN, OUTPUT);
  digitalWrite(LCD_BL_PIN, HIGH);
  tft.fillScreen(ST7735_BLACK);                       // Fill the screen with Black
  tft.setRotation(0);                                 // Set rotation to Portrait mode (set to '1' for Landscape)
  tft.setTextWrap(false);                             // By default long lines of text are NOT wrapped at the end of the line, set to 'TRUE' for wrapping the text
  tft.setTextSize(1);                                 // Set the initial text size to 1
  tft.setCursor(0,0);                                 // Initial Text Location
  tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
  tft.setTextSize(1);                                 // Set the text size to 1
  displayRunningSketch();
#else
#error unrecognized DSP for initDisplay
#endif
  popUpTimeoutID = -1;
  addEventHandler(EVT_DISPLAY_SAVER,saverTimeout,0,"display saver timeout event handler");
  addEventHandler(EVT_POPUP_DISPLAY,popUpTimeout,0,"popup display timeout event handler");
  addLoopScanner(60000,displayLoop,60000,"displayLoop_60000ms");
  SERIAL_PRINTLN("display initialized");
}




/* *****************************************************************************
 *    ST7735 TFT display functions
 * *****************************************************************************/


const int textxoffset = 0;
const int textyoffset = 0;

const int tft_line1 = 0;
const int tft_line4 = 39;
const int tft_line5 = 52;
const int tft_line6 = 65;
const int tft_line7 = 78;
const int tft_line8 = 91;

void setupTFT() {
  // initialize the screen
#if defined(DSP_ST7735)
  tft.initR(INITR_BLACKTAB);                          // initialize a ST7735S chip, black tab
  
  pinMode(LCD_BL_PIN, OUTPUT);
  digitalWrite(LCD_BL_PIN, HIGH);

  tft.fillScreen(ST7735_BLACK);                       // Fill the screen with Black
  tft.setRotation(0);                                 // Set rotation to Portrait mode (set to '1' for Landscape)
  tft.setTextWrap(false);                             // By default long lines of text are NOT wrapped at the end of the line, set to 'TRUE' for wrapping the text
  tft.setTextSize(1);                                 // Set the initial text size to 1
  tft.setCursor(textxoffset + 0,textyoffset + 0);     // Initial Text Location

  // Draw the 1.8" LED display layout using 24 bit colour (limitation of the ST7735S library)
  // tft.drawRect(0, 0, 159, 128, ST7735_CYAN);        // Draw a Rectangle  x,y, across, down for the Colour Values
  // tft.drawRoundRect(0, 0, 63, 31, 10, ST7735_CYAN);    // Draw a Rectangle  x,y, across, down with corner & Colour Values
  // tft.drawRoundRect(0, 35, 128, 125, 10, ST7735_CYAN); // Draw a Rectangle  x,y, across, down with corner & Colour Values
  // tft.drawLine(64, 40, 64, 150, ST7735_CYAN);          // Draw a mid-line  x1,y1, x2, y2 for the Colour Values

  tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
  tft.setTextSize(1);                                 // Set the text size to 1
  // tft.setCursor(textxoffset + 0, textyoffset + tft_line1 + 1);
  // tft.print("Line 1");
  // tft.setCursor(textxoffset + 0, textyoffset + tft_line4 + 1);
  // tft.print("Line 4");
#elif defined(DSP_ST7735_eSPI)
  tft.init(INITR_BLACKTAB);                          // initialize a ST7735S chip, black tab
  
  pinMode(LCD_BL_PIN, OUTPUT);
  digitalWrite(LCD_BL_PIN, HIGH);

  tft.fillScreen(ST7735_BLACK);                       // Fill the screen with Black
  tft.setRotation(0);                                 // Set rotation to Portrait mode (set to '1' for Landscape)
  tft.setTextWrap(false);                             // By default long lines of text are NOT wrapped at the end of the line, set to 'TRUE' for wrapping the text
  tft.setTextSize(1);                                 // Set the initial text size to 1
  tft.setCursor(textxoffset + 0,textyoffset + 0);     // Initial Text Location

  // Draw the 1.8" LED display layout using 24 bit colour (limitation of the ST7735S library)
  // tft.drawRect(0, 0, 159, 128, ST7735_CYAN);        // Draw a Rectangle  x,y, across, down for the Colour Values
  // tft.drawRoundRect(0, 0, 63, 31, 10, ST7735_CYAN);    // Draw a Rectangle  x,y, across, down with corner & Colour Values
  // tft.drawRoundRect(0, 35, 128, 125, 10, ST7735_CYAN); // Draw a Rectangle  x,y, across, down with corner & Colour Values
  // tft.drawLine(64, 40, 64, 150, ST7735_CYAN);          // Draw a mid-line  x1,y1, x2, y2 for the Colour Values

  tft.setTextColor(ST7735_GREEN, ST7735_BLACK);
  tft.setTextSize(1);                                 // Set the text size to 1
  // tft.setCursor(textxoffset + 0, textyoffset + tft_line1 + 1);
  // tft.print("Line 1");
  // tft.setCursor(textxoffset + 0, textyoffset + tft_line4 + 1);
  // tft.print("Line 4");
#else
#error unrecognized DSP for setupTFT
#endif
}

//
// ------------------------ end of module display.cpp -----------------------------------------
//
