//===========================================================================
//
// GPS Speedometer 2023 LEKtronics
// This is the final sketch !
//
// Crystalfontz Seeeduino / Arduino Simple Demonstration Program
// for FTDI / BridgeTek EVE graphic accelerators.
// FTDI Chip ID is FT811
// Code written for Seeeduino v4.2 set to 3.3v (important!)
// Seeeduino v4.2 is an Arduino Uno clone that can run at 3.3v.
//
// Added Adafruit GPS receiver and SD card reader 
// LEKtronics 2023
//---------------------------------------------------------------------------
// 2020-08-05 Brent A. Crosby / Crystalfontz America, Inc.
// https://www.crystalfontz.com/products/eve-accelerated-tft-displays.php
//---------------------------------------------------------------------------

//===========================================================================
#include <Arduino.h>
#include <SPI.h>
#include <stdarg.h>
// Definitions for our display.
#include "CFA10109_defines.h"
#include "CFAF240320A0_024Sx.h"

#include "LEK_GPS_240X320.h"
#include "LEK_DISH.h" 

#if BUILD_SD
#include <SD.h>
#endif

// The very simple EVE library files
#include "EVE_defines.h"
#include "EVE_base.h"
#include "EVE_draw.h"

// Our demonstrations of various EVE functions
#include "demos.h"


///GPS Stuff///////////////////////////////////////////////////////////////////////////

#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>

// Connect the GPS Power pin to 5V (White)
// Connect the GPS Ground pin to ground (Black)
// Connect the GPS TX (transmit) pin to Digital 5 (Yellow)
// Connect the GPS RX (receive) pin to Digital 4 (Green)

// You can change the pin numbers to match your wiring:
SoftwareSerial mySerial(5, 4);//RX_TX Green _ Yellow
// Connect to the GPS on the software port
Adafruit_GPS GPS(&mySerial);

// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO false

uint32_t timer = millis();
int gpsMph = 0;
int mphX1 = 0;
int mphX10 = 0;
int mphX100 = 0;
int gpsDir = 0; // compass heading every 45 degrees
int dirCP = 0; // compass point
int gpsElev = 0;
int gpsSatellites;

//===========================================================================
void setup()
  {
    
#if (DEBUG_LEVEL != DEBUG_NONE)
  // Initialize UART for debugging messages
  Serial.begin(115200);
#endif // (DEBUG_LEVEL != DEBUG_NONE)
  DBG_STAT("Begin\n");
   // 9600 NMEA is the default baud rate for Adafruit MTK GPS's- some use 4800

//-------------------------------------- GPS ------------------------------------------------------

  while (!Serial); // wait for Serial to be ready
  Serial.begin(115200);

   
  GPS.begin(9600);
  // uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  // uncomment this line to turn on only the "minimum recommended" data
  //GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);
  // For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since
  // the parser doesn't care about other sentences at this time
  // Set the update rate
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
  // For the parsing code to work nicely and have time to sort thru the data, and
  // print it out we don't suggest using anything higher than 1 Hz

  // Request updates on antenna status, comment out to keep quiet
  GPS.sendCommand(PGCMD_ANTENNA);

  // Ask for firmware version
  mySerial.println(PMTK_Q_RELEASE);

  //------------------------------------------------------------------------------------------------------
    

  //Initialize GPIO port states
  // Set CS# high to start - SPI inactive
  SET_EVE_CS_NOT;
  // Set PD# high to start
  SET_EVE_PD_NOT;
  SET_SD_CS_NOT;

  //Initialize port directions
  // EVE interrupt output (not used in this example)
  pinMode(EVE_INT, INPUT_PULLUP);
  // EVE Power Down (reset) input
  pinMode(EVE_PD_NOT, OUTPUT);
  // EVE SPI bus CS# input
  pinMode(EVE_CS_NOT, OUTPUT);
  // USD card CS
  pinMode(SD_CS, OUTPUT);
  // Optional pin used for LED or oscilloscope debugging.
  pinMode(DEBUG_LED, OUTPUT);

  // Initialize SPI
  SPI.begin();
  //Bump the clock to 8MHz. Appears to be the maximum.
  SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
  DBG_GEEK("SPI initialzed to: 8MHz\n");

#if BUILD_SD
  // The prototype hardware appears to functon fine at 8MHz which
  // also appears to be the max that the ATmega328P can do.
  if (!SD.begin(8000000,SD_CS))
    {
    DBG_STAT("uSD card failed to initialize, or not present\n");
    //Reset the SPI clock to fast. SD card library does not clean up well.
    //Bump the clock to 8MHz. Appears to be the maximum.
    SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
    }
  else
    {
    DBG_STAT("uSD card initialized.\n");
    }
#endif

  //See if we can find the FTDI/BridgeTek EVE processor
  if(0 != EVE_Initialize())
    {
    DBG_STAT("Failed to initialize %s8%02X. Stopping.\n",EVE_DEVICE<0x14?"FT":"BT",EVE_DEVICE);
    while(1);
    }
  else
    {
    DBG_STAT("%s8%02X initialized.\n",EVE_DEVICE<0x14?"FT":"BT",EVE_DEVICE);
    }
  } //  setup()


void loop()
  {
  DBG_GEEK("Loop initialization.\n");
  DBG_STAT("Initialization complete, entering main loop.\n");

  char c = GPS.read();
  // if you want to debug, this is a good time to do it!
  if ((c) && (GPSECHO))
    Serial.write(c);

  // if a sentence is received, we can check the checksum, parse it...
  if (GPS.newNMEAreceived()) {
    // a tricky thing here is if we print the NMEA sentence, or data
    // we end up not listening and catching other sentences!
    // so be very wary if using OUTPUT_ALLDATA and trytng to print out data
    //Serial.println(GPS.lastNMEA());   // this also sets the newNMEAreceived() flag to false

    if (!GPS.parse(GPS.lastNMEA()))   // this also sets the newNMEAreceived() flag to false
      return;  // we can fail to parse a sentence in which case we should just wait for another
  }
  // approximately every 2 seconds or so, print out the current stats
  if (millis() - timer > 2000) {
    timer = millis(); // reset the timer

    Serial.print("\nTime: ");
    if (GPS.hour < 10) { Serial.print('0'); }
    Serial.print(GPS.hour, DEC); Serial.print(':');
    if (GPS.minute < 10) { Serial.print('0'); }
    Serial.print(GPS.minute, DEC); Serial.print(':');
    if (GPS.seconds < 10) { Serial.print('0'); }
    Serial.print(GPS.seconds, DEC); Serial.print('.');
    if (GPS.milliseconds < 10) {
      Serial.print("00");
    } else if (GPS.milliseconds > 9 && GPS.milliseconds < 100) {
      Serial.print("0");
    }
    Serial.println(GPS.milliseconds);
    //Serial.print("Date: ");
    //Serial.print(GPS.day, DEC); Serial.print('/');
    //Serial.print(GPS.month, DEC); Serial.print("/20");
    //Serial.println(GPS.year, DEC);
    //Serial.print("Fix: "); Serial.print((int)GPS.fix);
    //Serial.print(" quality: "); Serial.println((int)GPS.fixquality);
    
     //Serial.println("Satellites: "); Serial.println((int)GPS.satellites);
     gpsSatellites = int(GPS.satellites);
     //gpsSatellites = 6;  //trouble shooting
     //Serial.println("Satellites: "); Serial.println(gpsSatellites);    
       
   if (GPS.fix) {
      //Serial.print("Location: ");
      //Serial.print(GPS.latitude, 4); Serial.print(GPS.lat);
      //Serial.print(", ");
      //Serial.print(GPS.longitude, 4); Serial.println(GPS.lon);
      
      //Serial.print("Speed (knots): "); Serial.println(GPS.speed);
      gpsMph = int(GPS.speed * 1.15078); //knots to mph  //// knots * 1.15078
      //gpsMph = 100; //for testing
      //Serial.print("Speed (mph): "); Serial.println(gpsMph);
      
      //Serial.print("Angle: "); Serial.println(GPS.angle);
      gpsDir = int(GPS.angle);
      //gpsDir = 315; // for testing
      dirCP = round(gpsDir / 45);
      
      //Serial.print("Altitude: "); Serial.println(GPS.altitude);
      gpsElev = int(GPS.altitude * 3.281); // meters to feet
      //Serial.print("Elevation (feet): "); Serial.println(gpsElev);
      

      } //end GPS.fix
   
      // display GPS data
        eveDisplay(); 
   
    }// end timer
       
  } //end loop()


  
//===========================================================================
void eveDisplay()
  {

    //Get the current write pointer from the EVE
  uint16_t
    FWo;
  FWo = EVE_REG_Read_16(EVE_REG_CMD_WRITE);
  DBG_GEEK("Initial Offest Read: 0x%04X = %u\n",FWo ,FWo);

  //Keep track of the RAM_G memory allocation
  uint32_t
    RAM_G_Unused_Start;
  RAM_G_Unused_Start=0;
  DBG_GEEK("Initial RAM_G: 0x%08lX = %lu\n",RAM_G_Unused_Start,RAM_G_Unused_Start);

#if (0 != LOGO_DEMO)
  DBG_STAT("Initialize_Logo_Demo() . . .");
  FWo=Initialize_Logo_Demo(FWo,&RAM_G_Unused_Start);
  DBG_STAT("  done.\n");
  DBG_GEEK("RAM_G after logo: 0x%08lX = %lu\n",RAM_G_Unused_Start,RAM_G_Unused_Start);
#endif // (0 != LOGO_DEMO)

#if (0 != IMAGE_DEMO)
  DBG_STAT("Initialize_Image_Demo() . . .");
  FWo=Initialize_Image_Demo(FWo,&RAM_G_Unused_Start);
  DBG_STAT("  done.\n");
  DBG_GEEK("RAM_G after image load: 0x%08lX = %lu\n",RAM_G_Unused_Start,RAM_G_Unused_Start);
  
#endif // (0 != IMAGE_DEMO)
  
    Serial.println ("eveDisplay");
  
    //========== FRAME SYNCHRONIZING ==========
    // Wait for graphics processor to complete executing the current command
    // list. This happens when EVE_REG_CMD_READ matches EVE_REG_CMD_WRITE, indicating
    // that all commands have been executed.  We have a local copy of
    // EVE_REG_CMD_WRITE in FWo.
    //
    // This appears to only occur on frame completion, which is nice since it
    // allows us to step the animation along at a reasonable rate.
    //
    // If possible, I have tweaked the timing on the Crystalfontz displays
    // to all have ~60Hz frame rate.
    FWo=Wait_for_EVE_Execution_Complete(FWo);



        //========== START THE DISPLAY LIST ==========
    // Start the display list
    FWo=EVE_Cmd_Dat_0(FWo,
                      (EVE_ENC_CMD_DLSTART));
  
    // Set the default clear color to black
    FWo=EVE_Cmd_Dat_0(FWo,
                      EVE_ENC_CLEAR_COLOR_RGB(0,0,0));
    // Clear the screen - this and the previous prevent artifacts between lists
    FWo=EVE_Cmd_Dat_0(FWo,
                      EVE_ENC_CLEAR(1 /*CLR_COL*/,1 /*CLR_STN*/,1 /*CLR_TAG*/));
                      
    //========== ADD GRAPHIC ITEMS TO THE DISPLAY LIST ==========
    //Fill background with white
    FWo=EVE_Filled_Rectangle(FWo,
                             0,0,LCD_WIDTH-1,LCD_HEIGHT-1);
                             
    #if (0 != LOGO_DEMO)
    FWo=Add_Logo_To_Display_List(FWo);
    #endif // (0 != LOGO_DEMO)

            //Set font color to red              
        FWo=EVE_Cmd_Dat_0(FWo,
                              EVE_ENC_COLOR_RGB(0xFF,0x00,0x00));

        // Set font 16 to 34 size
        FWo=EVE_Cmd_Dat_2(FWo,
                              EVE_ENC_CMD_ROMFONT,16,34);
                                  
        // display gpsMph from GPS
        FWo=EVE_PrintF(FWo,
                          120,  //X
                          64,  //Y
                          16,         //Font
                           EVE_OPT_CENTER, //Options
                          "%d",//"%d MPH"
                          gpsMph); 

//++++++++++++++++++++++++++++++++++++++++++++ HEADING +++++++++++++++++++++++++++++++++++++++++++++
        // display Heading from GPS
        FWo=EVE_PrintF(FWo,
                          130,
                          140,
                          28,
                          EVE_OPT_CENTER,
                          "%d",
                          gpsDir);  

  //dirCP = 1; //troubleshooting                       

 switch (dirCP){

    case 0:
          FWo=EVE_PrintF(FWo,
                          60,
                          143,
                          26,
                          EVE_OPT_CENTER,
                          "NORTH",
                          "");
      break;
     
    case 1:
          FWo=EVE_PrintF(FWo,
                          60,
                          143,
                          26,
                          EVE_OPT_CENTER,
                          "NORTH EAST",
                          "");
      break;

      
    case 2:
          FWo=EVE_PrintF(FWo,
                          60,
                          143,
                          26,
                          EVE_OPT_CENTER,
                          "EAST",
                          "");
      break;
    case 3:
          FWo=EVE_PrintF(FWo,
                          60,
                          143,
                          26,
                          EVE_OPT_CENTER,
                          "SOUTH EAST",
                          "");
      break;
    case 4:
          FWo=EVE_PrintF(FWo,
                          60,
                          143,
                          26,
                          EVE_OPT_CENTER,
                          "SOUTH",
                          "");
      break;
    case 5:
          FWo=EVE_PrintF(FWo,
                          60,
                          143,
                          26,
                          EVE_OPT_CENTER,
                          "SOUTH WEST",
                          "");
      break;
    case 6:
          FWo=EVE_PrintF(FWo,
                          60,
                          143,
                          26,
                          EVE_OPT_CENTER,
                          "WEST",
                          "");
      break;
    case 7:
          FWo=EVE_PrintF(FWo,
                          60,
                          143,
                          26,
                          EVE_OPT_CENTER,
                          "NORTH WEST",
                          "");
      break;
    case 8:
          FWo=EVE_PrintF(FWo,
                          60,
                          143,
                          26,
                          EVE_OPT_CENTER,
                          "NORTH",
                          "");
      break; 
   }    
                                               
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++                              
        // display Elevation from GPS
        FWo=EVE_PrintF(FWo,
                          120,
                          182,
                          28,
                          EVE_OPT_CENTER,
                          "%d",
                          gpsElev);
                         

    #if (0 != IMAGE_DEMO)
    FWo=Add_Image_To_Display_List(FWo);
    #endif // (0 != IMAGE_DEMO) 

        if (gpsSatellites >= 6) { 
       
        
        //Set font color to black
        FWo=EVE_Cmd_Dat_0(FWo,
                             EVE_ENC_COLOR_RGB(0x00,0x00,0x00));                        

                // display # of satellites from GPS
        FWo=EVE_PrintF(FWo,
                          214,  //X
                          228,  //Y
                          31,         //Font
                          EVE_OPT_CENTER, //Options
                          "+",
                          "");  
        }  
                             

       //========== FINSH AND SHOW THE DISPLAY LIST ==========
    // Instruct the graphics processor to show the list
    FWo=EVE_Cmd_Dat_0(FWo, EVE_ENC_DISPLAY());
    // Make this list active
    FWo=EVE_Cmd_Dat_0(FWo, EVE_ENC_CMD_SWAP);
    // Update the ring buffer pointer so the graphics processor starts executing
    EVE_REG_Write_16(EVE_REG_CMD_WRITE, (FWo));  
   

  } // eveDisplay()
//===========================================================================
