XRAD'S arduino serial Plotter -> instead to TFT?

can anyone suggest a method for getting the type of serial monitor display for arduino serial plotter real time graphing instead to display to TFT? Maybe this can help me in my GPS code issue described below?

I am trying to create a moving GPS map where the map center pixel remains in position while the OLD 'map track' continues to redraw itself moving away from the center pixel. This concept I think requires saving new x/y coord, plotting/drawing new x/y coord, erasing and then redrawing all saved coords PLUS newest ones, all x/y points now in new positions, WHILE maintaining the newest point at TFT center.

It's a scrolling real-time map basically. But I am having difficulty with the underlying concept....

  1. Should I redraw all points in relation to closest neighbor point?
  2. should I redraw all points in relation to map center x/y?

Here is my build so far. It draws new points in relation to the original screen center...but the map of points grows away from the center point RATHER than the center point always being the newest point.

see: XRAD'S REALLY COOL GPS TRACKER - adafruit industries

any pointers or code tips would be much appreciated!!!

current code:

/*  XRAD'S REALLY COOL GPS TRACKER 1/28/22 using adafruit 3.5 touch
  tft breakout, adafruit STMPE610, Teensy 3.6 w/SD. This program
  obtains GPS data in decimal degrees and converts these to
  approximate planar 'cartesian' x/y coordinates. It logs your
  initial location and then draws a map of your journey. The
  program also allows you to real-time scale the map to approximate
  radii of 100meters, 5 miles, 15 miles, and 30 miles...and
  zoom in and out of the scales.  This is done by saving the
  converted x, y coords to SD and then redrawing them via a
  buffer as pixels to the tft.  Additionally, I use adafruit
  drawButtons off the arduino examples to show how to use the
  touch screen to scale the map, color a text box, and delete/save
  the SD file.  In order to keep the button press functions activating
  in quick fashion, the loop is paused while you select button choices.
  You must return to "GPS ACTIVE" to start taking readings again.
  On Boot, code draws last saved map at 100m radius.
  It's one of my favorite cool project combining a few really neat
  adafruit codes and products!! Enjoy!

  hardware:
  Teensy 3.6, or you can use a 4.1
  Micro SD card
  Adafruit 3.5 tft touch breakout(or you could use a 3.5 tft feather)
  Adafruit stmpe610 breakout (good luck finding these..better get a 3.5 feather!)
  Adafruit INA219 current sensor
  Goouuu Tech GT-u7 GPS module (very nice!)
  Pololu mini push button power switch
  120mm aluminum case
  Pololu 5v regulator
  DFRobot DFR0564 2s lipo charger, I broke out the charging led to side panel
  micro USB panel mount extension 30cm

  things to do??
  1) save more than one map and name map via buttons
  2) recall selected map
  3) draw recalled map AND center x/y pixel on the last GPS saved coords (ie: continue your journey)
  4) draw map color selections?
  5) wifi link to google maps?
  5) and it never ends...
*/

#include <SD.h>
#include <TinyGPS.h>
#include <FastLED.h>//just used for timer!

#include "Adafruit_GFX.h"
#include "Adafruit_HX8357.h"
#include <Adafruit_STMPE610.h>
#include <Adafruit_INA260.h>

#define TFT_CS 10
#define TFT_DC 9
#define TFT_RST -1 // RST can be set to -1

Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST);
Adafruit_STMPE610 touch = Adafruit_STMPE610();
Adafruit_INA260 ina260 = Adafruit_INA260();


// For rotation 3
#define TS_MINX 3800
#define TS_MAXX 100
#define TS_MINY 100
#define TS_MAXY 3750


TinyGPS gps;

bool  oneTimePosition = true;
int long oneTimeLat, oneTimeLong;

unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 1000;

#define  R 6367000 // Radius earth in m

int long x ;
int long y ;
int long homex;
int long homey;

int dx;
int dy;

//not used yet
#define MAP_WIDTH 280
#define MAP_HEIGHT 280
#define MAP_CENTERX 250
#define MAP_CENTERY 140

//map x/y boundaries
#define MAP_X_MIN 111
#define MAP_X_MAX 389
#define MAP_Y_MIN 1
#define MAP_Y_MAX 279

int counter = 0;
int oldCounter;

File myFile;//SD file

String buffer;//buffer to redraw saved SD data


//define some colors, not all are used
#define HX8357_BLACK       0x0000  ///<   0,   0,   0
#define HX8357_NAVY        0x000F  ///<   0,   0, 123
#define HX8357_DARKGREEN   0x03E0  ///<   0, 125,   0
#define HX8357_DARKCYAN    0x03EF  ///<   0, 125, 123
#define HX8357_MAROON      0x7800  ///< 123,   0,   0
#define HX8357_PURPLE      0x780F  ///< 123,   0, 123
#define HX8357_OLIVE       0x7BE0  ///< 123, 125,   0
#define HX8357_LIGHTGREY   0xC618  ///< 198, 195, 198
#define HX8357_DARKGREY    0x7BEF  ///< 123, 125, 123
#define HX8357_BLUE        0x001F  ///<   0,   0, 255
#define HX8357_GREEN       0x07E0  ///<   0, 255,   0
#define HX8357_CYAN        0x07FF  ///<   0, 255, 255
#define HX8357_RED         0xF800  ///< 255,   0,   0
#define HX8357_MAGENTA     0xF81F  ///< 255,   0, 255
#define HX8357_YELLOW      0xFFE0  ///< 255, 255,   0
#define HX8357_WHITE       0xFFFF  ///< 255, 255, 255
#define HX8357_ORANGE      0xFD20  ///< 255, 165,   0
#define HX8357_GREENYELLOW 0xAFE5  ///< 173, 255,  41
#define HX8357_PINK        0xFC18  ///< 255, 130, 198

//define buttons RIGHT side of 3.5 tft(rotation 3)
#define BUTTON_X 440
#define BUTTON_Y 40
#define BUTTON_W 80
#define BUTTON_H 80
#define BUTTON_TEXTSIZE 2
#define DISPLAY_XOFFSET 80
#define DISPLAY_TEXTOFFSET 90
#define DISPLAY_YOFFSET 0

enum ButtonName {
  BTN_UP,
  BTN_SELECT,
  BTN_DOWN,
  BTN_MENU
};


int menuCount = 0;


//button menus
//the looping button choice, you can only choose MENU
#define MENU0_BTN_CNT 4
Adafruit_GFX_Button Menu0Buttons[MENU0_BTN_CNT];
char Menu0Labels[MENU0_BTN_CNT][5] = {"", "", "", "MENU"};
uint16_t Menu0Colors[MENU0_BTN_CNT] = {HX8357_BLACK, HX8357_BLACK,
                                       HX8357_BLACK, HX8357_ORANGE
                                      };

//ZOOM buttons
#define MENU1_BTN_CNT 4
Adafruit_GFX_Button Menu1Buttons[MENU1_BTN_CNT];
char Menu1Labels[MENU1_BTN_CNT][5] = {"ZMOT", "Sel", "ZMIN", "MENU"};
uint16_t Menu1Colors[MENU1_BTN_CNT] = {HX8357_DARKGREY, HX8357_DARKGREY,
                                       HX8357_DARKGREY, HX8357_ORANGE
                                      };

//COLOR buttons
#define MENU2_BTN_CNT 4
Adafruit_GFX_Button Menu2Buttons[MENU2_BTN_CNT];
char Menu2Labels[MENU2_BTN_CNT][5] = {"Up", "Sel", "Down", "MENU"};
uint16_t Menu2Colors[MENU2_BTN_CNT] = {HX8357_BLUE, HX8357_BLUE,
                                       HX8357_BLUE, HX8357_ORANGE
                                      };

//save or delte SD GPS file
#define MENU3_BTN_CNT 4
Adafruit_GFX_Button Menu3Buttons[MENU3_BTN_CNT];
char Menu3Labels[MENU3_BTN_CNT][5] = {"YES", "Sel", "NO", "MENU"};
uint16_t Menu3Colors[MENU3_BTN_CNT] = {HX8357_RED, HX8357_RED,
                                       HX8357_RED, HX8357_ORANGE
                                      };


int textSize = 2;//starting txt size for bottom textbox, but really the only size I use
int textColorIndex = 0;

//menu 2 color selections
uint16_t textColor[7] = {
  HX8357_WHITE,
  HX8357_RED,
  HX8357_GREEN,
  HX8357_BLUE,
  HX8357_CYAN,
  HX8357_MAGENTA,
  HX8357_YELLOW
};

//-------------------------------------------------------------------------
void setTextColorIndex(int updown) {
  textColorIndex += updown;
  if (textColorIndex > 6)
    textColorIndex = 0;
  else if (textColorIndex < 0)
    textColorIndex = 6;
  tft.setTextColor(textColor[textColorIndex]);
}

//-------------------------------------------------------------------------
void setTextSizeIndex(int updown) {
  tft.setTextSize(2);
}

//-------------------------------------------------------------------------
bool initializeButtons(
  Adafruit_GFX_Button menuButtons[],
  uint16_t menuColors[],
  char menuLabels[][5],
  int menuButtonCount) {
  //tft.fillScreen(HX8357_BLACK);
  //tft.fillRect(460, 0, 20, 320, HX8357_BLACK);

  for (uint8_t row = 0; row < menuButtonCount; row++)
  {
    menuButtons[row].initButton(& tft,
                                BUTTON_X,
                                BUTTON_Y + row * (BUTTON_H),
                                BUTTON_W,
                                BUTTON_H,
                                HX8357_BLACK,
                                menuColors[row],
                                HX8357_WHITE,
                                menuLabels[row], BUTTON_TEXTSIZE);
    menuButtons[row].drawButton();
  }
  return true;
}

//-------------------------------------------------------------------------
void tftSetCenterCursor(String str, int16_t xIn, int16_t yIn) {
  int16_t xText, yText;
  uint16_t w, h;
  tft.getTextBounds(str, 0, 0, &xText, &yText, &w, &h);
  tft.setCursor(80, 295);
}

//--------------------------------------------------------------------------------
void tftPrint(String str) {
  int16_t xText, yText;
  uint16_t w, h;
  tft.getTextBounds(str, 0, 0, &xText, &yText, &w, &h);
  tft.fillRect(  80,  290, 320, 25, HX8357_BLACK);
  tft.print(str);
}

//-------------------------------------------------------------------------
void tftCenterPrint(String str) {
  tft.setTextSize(textSize);
  tftSetCenterCursor(str,
                     DISPLAY_TEXTOFFSET + (  tft.width() - DISPLAY_TEXTOFFSET) / 2,
                     DISPLAY_YOFFSET + ( tft.height() - DISPLAY_YOFFSET) / 2);
  tftPrint(str);
}

//--------Are you pushing my buttons???---------------------------------
int  tftButtonRelease(Adafruit_GFX_Button menuButtons[], int menuButtonCount) {
  int btn = -1;
  TS_Point p;

  if (touch.bufferSize())
  {
    p = touch.getPoint();
  }
  else
  {
    // this is our way of tracking touch 'release'!
    p.x = p.y = p.z = -1;
  }

  // Scale from ~0->4000 to  tft.width using the calibration #'s
  if (p.z != -1)
  {
    int16_t px = p.x;
    int16_t py = p.y;
    p.x = map(py, TS_MINY, TS_MAXY, 0,  tft.width());
    p.y = map(px, TS_MINX, TS_MAXX, 0,  tft.height());
  }

  // go thru all the buttons, checking if they were pressed
  for (uint8_t b = 0; b < menuButtonCount; b++)
  {
    if (menuButtons[b].contains(p.x, p.y))
    {
      //Serial.print("Pressing: "); Serial.println(b);
      menuButtons[b].press(true);  // tell the button it is pressed
    }
    else
    {
      menuButtons[b].press(false);  // tell the button it is NOT pressed
    }
  }

  // now we can ask the buttons if their state has changed
  for (uint8_t b = 0; b < menuButtonCount; b++)
  {
    if (menuButtons[b].justReleased())
    {
      //Serial.print("Released: "); Serial.println(b);
      menuButtons[b].drawButton();  // draw normal
      btn = b;
    }

    if (menuButtons[b].justPressed())
    {
      menuButtons[b].drawButton(true);  // draw invert!
      delay(100); // UI debouncing
    }
  }
  return btn;
}



//-----MENU 3: SAVE OR CLEAR SD----------------------------
void processMenu3() {
  String msg = "";
  bool exitLoop = false;
  bool clearSDMap = false;

  initializeButtons(Menu3Buttons, Menu3Colors, Menu3Labels, MENU3_BTN_CNT);

  msg = "Menu 3: CLEAR SD FILE";
  //Serial.println(msg);
  setTextColorIndex(0);
  setTextSizeIndex(0);
  tftCenterPrint(msg);

  while (!exitLoop)
  {
    int btn =  tftButtonRelease(Menu3Buttons, MENU3_BTN_CNT);
    if (btn >= 0 && btn < MENU3_BTN_CNT)
    {
      //Serial.print("btn = "); Serial.println(btn);
    }

    switch (btn)
    {
      case BTN_UP:
        msg = "OK TO CLEAR SD MAP FILE";
        //Serial.println(msg);
        setTextColorIndex(0);
        tftCenterPrint(msg);
        clearSDMap = true;
        break;

      case BTN_SELECT:
        if (clearSDMap == false) {
          msg = "SAVE SD MAP FILE";
          //Serial.println(msg);
          setTextColorIndex(0);
          tftCenterPrint(msg);
          delay(1000);
          msg = "Press MENU";
          //Serial.println(msg);
          setTextColorIndex(0);
          tftCenterPrint(msg);
          //break;
        }

        else if (clearSDMap == true) {
          msg = "CLEARING SD MAP FILE";
          //Serial.println(msg);
          setTextColorIndex(0);
          tftCenterPrint(msg);
          SD.remove("GPS");//remove prior old GPS SD file on button press
          clearMap();
          drawMap();
          delay (1000);
          msg = "Press MENU";
          //Serial.println(msg);
          setTextColorIndex(0);
          tftCenterPrint(msg);
          clearSDMap = false;
        }
        break;


      case BTN_DOWN:
        msg = "SAVE SD MAP FILE";
        //Serial.println(msg);
        setTextColorIndex(0);
        tftCenterPrint(msg);
        clearSDMap = false;
        break;

      case BTN_MENU:
        menuCount = menuCount + 1;
        exitLoop = true;
        break;

      default:
        break;
    }
  }
}



//------MENU 2: COLOR CHOICES FOR TXTBOX------------------------------
void processMenu2() {
  String msg = "";
  bool exitLoop = false;

  initializeButtons(Menu2Buttons, Menu2Colors, Menu2Labels, MENU2_BTN_CNT);

  msg = "Menu 2: COLOR";
  //Serial.println(msg);
  setTextColorIndex(0);
  setTextSizeIndex(0);
  tftCenterPrint(msg);

  while (!exitLoop)
  {
    int btn =  tftButtonRelease(Menu2Buttons, MENU2_BTN_CNT);
    if (btn >= 0 && btn < MENU2_BTN_CNT)
    {
      // Serial.print("btn = "); Serial.println(btn);
    }

    switch (btn)
    {
      case BTN_UP:
        msg = "Menu 2: Color Up button";
        //Serial.println(msg);
        setTextColorIndex(1);
        tftCenterPrint(msg);
        break;

      case BTN_SELECT:
        msg = "Color Selected. Press MENU";
        //Serial.println(msg);
        setTextColorIndex(0);
        tftCenterPrint(msg);
        break;

      case BTN_DOWN:
        msg = "Menu 2: Color Down Button";
        //Serial.println(msg);
        setTextColorIndex(-1);
        tftCenterPrint(msg);
        break;

      case BTN_MENU:
        menuCount = menuCount + 1;
        exitLoop = true;
        break;

      default:
        break;
    }
  }
}

//---------MENU 1: ZOOM IN OUT--------------------------------
void processMenu1() {
  String msg = "";
  bool exitLoop = false;

  initializeButtons(Menu1Buttons, Menu1Colors, Menu1Labels, MENU1_BTN_CNT);

  msg = "Menu 1: ZOOM";
  //Serial.println(msg);
  setTextColorIndex(0);
  setTextSizeIndex(0);
  tftCenterPrint(msg);
  ZOOM();

  while (!exitLoop)
  {
    int btn =  tftButtonRelease(Menu1Buttons, MENU1_BTN_CNT);
    if (btn >= 0 && btn < MENU1_BTN_CNT)
    {
      // Serial.print("btn = "); Serial.println(btn);
    }

    switch (btn)
    {
      case BTN_UP:
        counter = counter + 1;
        if (counter > 3) {
          counter = 3;
        }
        if (counter == 1) {
          msg = "ZOOM OUT 5 mile radius";//these are ALL approximate r's
        }
        else if (counter == 2) {
          msg = "ZOOM OUT 15 mile radius";
        }
        else if (counter == 3) {
          msg = "ZOOM OUT 30 mile radius";
        }
        ZOOM();
        //Serial.println(msg);
        setTextColorIndex(0);
        setTextSizeIndex(0);
        tftCenterPrint(msg);
        break;


      case BTN_DOWN:
        counter = counter - 1;
        if (counter < 0) {
          counter = 0;
        }
        if (counter == 0) {
          msg = "ZOOM IN 100 meter radius";
        }
        else if (counter == 1) {
          msg = "ZOOM IN 5 mile radius";
        }
        else if (counter == 2) {
          msg = "ZOOM IN 15 mile radius";
        }
        ZOOM();
        //Serial.println(msg);
        setTextColorIndex(0);
        setTextSizeIndex(0);
        tftCenterPrint(msg);
        break;

      case BTN_SELECT:
        msg = "Zoom Selected. Press MENU";
        //Serial.println(msg);
        setTextColorIndex(0);
        setTextSizeIndex(0);
        tftCenterPrint(msg);
        break;

      case BTN_MENU:
        menuCount = menuCount + 1;
        exitLoop = true;
        break;

      default:
        break;
    }
  }
}


//MENU 0----THE LOOPING MENU FOR 'MENU' SELECTIONS----
//you must be in this menu for the GPS to be active!!
void processMenu0() {
  String msg = "";
  int btn =  tftButtonRelease(Menu0Buttons, MENU0_BTN_CNT);

  if (btn >= 0 && btn < MENU0_BTN_CNT)
  {
    //Serial.print("btn = "); Serial.println(btn);
  }

  switch (btn)
  {
    case BTN_UP:
      break;

    case BTN_DOWN:
      break;

    case BTN_MENU:
      msg = "GPS ACTIVE";
      //Serial.println(msg);
      setTextColorIndex(0);
      tftCenterPrint(msg);
      menuCount = menuCount + 1;

      if (menuCount == 1) {
        initializeButtons(Menu1Buttons, Menu1Colors, Menu1Labels, MENU1_BTN_CNT);
        processMenu1();
      }

      if (menuCount == 2) {
        initializeButtons(Menu2Buttons, Menu2Colors, Menu2Labels, MENU2_BTN_CNT);
        processMenu2();
      }
      if (menuCount == 3) {
        initializeButtons(Menu3Buttons, Menu3Colors, Menu3Labels, MENU3_BTN_CNT);
        processMenu3();
      }

      if (menuCount > 3) {
        initializeButtons(Menu0Buttons, Menu0Colors, Menu0Labels, MENU0_BTN_CNT);
        setTextColorIndex(0);
        tftCenterPrint("GPS ACTIVE");
        menuCount = 0;
        processMenu0();
      }
      break;


    case BTN_SELECT:
      break;

    default:
      break;
  }
}





void clearMap() {
  tft.fillRoundRect(111, 1, 278, 278, 10, HX8357_BLACK);
}



void drawMap() { //draw a map w/dotted cross
  tft.drawRoundRect(110, 0, 280, 280, 10, HX8357_GREEN);
  for (int x = 55; x < 195; x++) {
    tft.drawPixel((2 * x), 140, HX8357_GREEN);
  }
  for (int y = 0; y < 140; y++) {
    tft.drawPixel(250, (2 * y), HX8357_GREEN);
  }
  tft.setCursor(245, 2);
  tft.fillRect(245, 2, 10, 14, HX8357_BLACK);
  tft.setTextColor(HX8357_GREEN);  tft.setTextSize(2);
  tft.println("N");
}



void drawSetup() {

  tft.setCursor(0, 200);
  tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
  tft.print("ZOOM I/O : ");
  tft.setCursor(0, 215);
  tft.print("RADIANS X: ");
  tft.setCursor(0, 230);
  tft.print("RADIANS y: ");

  tft.setCursor(0, 165);
  tft.print("X: ");
  tft.setCursor(0, 180);
  tft.print("Y: ");

  tft.setCursor(0, 45);
  tft.print("SATELITES: ");
  tft.setCursor(0, 60);
  tft.print("PRECISION: ");
}


void ZOOM() {
  if (counter != oldCounter) {
    clearMap();
    drawMap();

    // re-open the file for reading:
    myFile = SD.open("GPS");
    if (myFile) {
      //Serial.println("GPS:");

      drawMap();

      // read from the file until there's nothing else in it, line by line:
      while (myFile.available()) {
        //Serial.write(myFile.read());

        buffer = myFile.readStringUntil('\n');

        //Serial.println(buffer); //debugging compare to scaled x/y

        char *x_str;
        x_str = strtok((char*)buffer.c_str(), ",");//string tokens and parse line
        int SDdx = atoi(x_str);//convert to int

        char *y_str;
        y_str = strtok(NULL, ",");//string token rest of line
        int SDdy = atoi(y_str);

        //for scaling: calculate SDdx here
        if (counter == 1) { // ~  5 mile map radius
          SDdx =  SDdx / 100;
          SDdx =  SDdx + 250;
        }
        else if (counter == 2) { // ~ 15 mile map radius
          SDdx =  SDdx / 300;
          SDdx =  SDdx + 250;
        }
        else if (counter == 3) { // ~ 30 mile map radius
          SDdx =  SDdx / 500;
          SDdx =  SDdx + 250;
        }
        //for scaling: calculate SDdy here
        if (counter == 1) { // ~  5 mile map radius
          SDdy =  SDdy / 100;
          SDdy =  SDdy + 140;//don't reverse N / S map plot, it's already calculated
        }
        else if (counter == 2) { // ~ 15 mile map radius
          SDdy =  SDdy / 300;
          SDdy =  SDdy + 140;
        }
        else if (counter == 3) { // ~ 30 mile map radius
          SDdy =  SDdy / 500;
          SDdy =  SDdy + 140;
        }
        //draw pixels within map boundaries
        if (((SDdx > MAP_X_MIN) && (SDdx < MAP_X_MAX)) && ((SDdy > MAP_Y_MIN) && (SDdy < MAP_Y_MAX))) {
          tft.drawPixel( SDdx, SDdy, HX8357_WHITE);
        }
        //compare to initial buffer x/y
        //Serial.print("SDdx: ");
        //Serial.println(SDdx);
        //Serial.print("SDdy: ");
        //Serial.println(SDdy);
      }

      // close the file:
      myFile.close();
    } else {
      // if the file didn't open, print an error:
      //Serial.println("error opening test.txt");
    }

    tft.fillRect(60, 200, 40, 55, HX8357_BLACK);
    tft.setCursor(60, 200);
    tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
    //tft.print("ZOOM I/O: ");//
    tft.println(counter);

    oldCounter = counter;
  }
}

//draw saved SD map on BOOT, at 100m radius
void BOOTDrawMap() {
  clearMap();
  drawMap();

  // re-open the file for reading:
  myFile = SD.open("GPS");
  if (myFile) {
    //Serial.println("GPS:");

    drawMap();

    // read from the file until there's nothing else in it, line by line:
    while (myFile.available()) {
      //Serial.write(myFile.read());

      buffer = myFile.readStringUntil('\n');

      //Serial.println(buffer); //debugging compare to scaled x/y

      char *x_str;
      x_str = strtok((char*)buffer.c_str(), ",");
      int SDdx = atoi(x_str);

      char *y_str;
      y_str = strtok(NULL, ",");
      int SDdy = atoi(y_str);


      //draw pixels within map boundaries
      if (((SDdx > MAP_X_MIN) && (SDdx < MAP_X_MAX)) && ((SDdy > MAP_Y_MIN) && (SDdy < MAP_Y_MAX))) {
        tft.drawPixel( SDdx, SDdy, HX8357_WHITE);
      }
    }

    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    //Serial.println("error opening test.txt");
  }

  tft.fillRect(60, 200, 40, 55, HX8357_BLACK);
  tft.setCursor(60, 200);
  tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
  //tft.print("ZOOM I/O: ");//
  tft.println(counter);
}

void ina610() {       //error should be the ina219!..only used for debugging
  Serial.print("Current: ");
  Serial.print(ina260.readCurrent());
  Serial.println(" mA");

  Serial.print("Bus Voltage: ");
  Serial.print(ina260.readBusVoltage());
  Serial.println(" mV");

  Serial.print("Power: ");
  Serial.print(ina260.readPower());
  Serial.println(" mW");

  Serial.println();
}



////////////////SETUP--do everything once////////////////////////
void setup()
{
  Serial.begin(115200);
  Serial.flush();

  Serial1.begin(9600);//my GPS device uses 9600 baud, using teensy Serial1
  delay(100);

  //Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(BUILTIN_SDCARD)) {
    // Serial.println("Card failed, or not present");
    tft.begin();
    tft.setRotation(3);
    tft.fillScreen(HX8357_BLACK);
    tft.setCursor(80, 100);
    tft.setTextColor(HX8357_WHITE);  tft.setTextSize(3);
    tft.println("POWER OFF AND");
    tft.setCursor(80, 160);
    tft.println("INSERT SD CARD");
    for (;;);//break loop/setup for EVERRRRRR!
  }
  Serial.println("card initialized.");
  //Serial.println("x,y");

  myFile = SD.open("GPS", FILE_WRITE);

  touch.begin();

  ina260.begin();

  /*
    if (!ina260.begin()) {
      Serial.println("Couldn't find INA260 chip");
      while (1);
    }
    Serial.println("INA260 found");

    if (! touch.begin()) {
      Serial.println("STMPE not found!");
      while (1);
    }
    Serial.println("STMPE found");
  */


  tft.begin();
  tft.setTextWrap(false);
  tft.setRotation(3);
  tft.fillScreen(HX8357_BLACK);
  tft.setCursor(80, 100);
  tft.setTextColor(HX8357_WHITE);
  tft.setTextSize(4);
  tft.println("GPS ACTIVATED");
  delay(2000);
  tft.fillScreen(HX8357_BLACK);

  tft.setCursor(0, 0);
  tft.setTextColor(HX8357_GREEN);
  tft.setTextSize(1);
  tft.print("CURRENT POSITION: ");
  tft.setCursor(0, 90);
  tft.print("INITIAL POSITION: ");
  tft.setCursor(0, 150);
  tft.print("CARTESIAN COORD: ");
  tft.setCursor(0, 255);
  tft.print("BATTERY VOLTAGE: ");

  //clear radians tft X Y ...
  tft.fillRect(60, 200, 40, 55, HX8357_BLACK);
  tft.setCursor(0, 200);
  tft.setTextColor(HX8357_WHITE);
  tft.setTextSize(1);
  tft.print("ZOOM I/O : ");
  tft.setCursor(0, 215);
  tft.print("RADIANS X: ");
  tft.setCursor(0, 230);
  tft.print("RADIANS y: ");

  tft.setCursor(0, 295);
  tft.setTextColor(HX8357_WHITE);  tft.setTextSize(2);
  tft.print("INFO: ");
  //tft.println(counter);


  BOOTDrawMap();//draw the saved SD map at 100m radius

  initializeButtons(Menu0Buttons, Menu0Colors, Menu0Labels, MENU0_BTN_CNT);
  startMillis = millis();
  setTextColorIndex(0);
  tftCenterPrint("GPS SEARCHING PLEASE WAIT");
}


///////////LOOP--run for everrrrrr///////////////////////
void loop() {
  processMenu0();

  bool newData = false;

  //   get GPS data every x seconds and report some key values
  EVERY_N_MILLISECONDS(4000) { //from fastLed lib

    for (unsigned long start = millis(); millis() - start < 1000;)//~minimum time to get data
    {
      while (Serial1.available())
      {
        char c = Serial1.read();
        if (gps.encode(c)) // Did a new valid sentence come in?
          newData = true;
      }
    }

    if (newData) {

      float flat, flon;
      unsigned long age;
      gps.f_get_position(&flat, &flon, &age);

      tft.fillRect(20, 15, 90, 10, HX8357_BLACK);
      tft.setCursor(0, 15);
      tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
      tft.print("LAT: ");
      tft.println(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flat, 6);

      tft.fillRect(20, 30, 90, 10, HX8357_BLACK);
      tft.setCursor(0, 30);
      tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
      tft.print("LON: ");
      tft.println(flon == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flon, 6);

      //record initial home position and display data on tft
      if (oneTimePosition == true)  {
        tft.setCursor(0, 105);
        tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
        tft.print("LAT: ");
        tft.println(flat, 6);
        tft.setCursor(0, 120);
        tft.print("LON: ");
        tft.println(flon, 6);

        //several ways of doing this.....
        float radiansX = ( flat * ((asin(1)) / 90));//arc sin(1) = π/2 radians = 90
        float radiansY = ( flon * ((asin(1)) / 90));

        homex = R * radiansY * cos(radiansX);
        homey = R * radiansX;

        tftCenterPrint("GPS ACTIVE");

        oneTimePosition = false;
      }

      //for real time GPS to x/y for TFT draw pixel
      float radiansX = ( flat * (asin(1)) / 90 );
      float radiansY = ( flon * (asin(1)) / 90 );

      x = R * radiansY * cos(radiansX);
      y = R * radiansX;

      if (homex != x) {
        dx = (x - homex);
        dx = dx + 250;
      }
      if (homey != y) {
        dy = (y - homey)  ;
        dy = -dy + 140;//reverse N/S here
      }


      myFile = SD.open("GPS", FILE_WRITE);//save the base scale x/y to SD

      // if the file opened okay, write to it:
      if (myFile) {
        myFile.print (dx);
        myFile.print (", ");
        myFile.println(dy);
        // close the file:
        myFile.close();
      } else {
        // if the file didn't open, print an error:
        //Serial.println("error opening test.txt");
      }

      //for scaling
      if (homex != x) {
        dx = (x - homex);
        //calculate dx/dy here for range
        if (counter == 0) { // ~  50 meter radius
          dx =  dx;
        }
        if (counter == 1) { // ~  5 mile map radius
          dx =  dx / 100;
        }
        if (counter == 2) { // ~ 15 mile map radius
          dx =  dx / 300;
        }
        if (counter == 3) { // ~ 30 mile map radius
          dx =  dx / 500;
        }
        dx =  dx + 250;
      }


      if (homey != y) {
        dy = (y - homey);
        //calculate dx/dy here for range
        if (counter == 0) { // ~  50 meter radius
          dy =  dy;
        }
        if (counter == 1) { // ~  5 mile map radius
          dy =  dy / 100;
        }
        if (counter == 2) { // ~ 15 mile map radius
          dy =  dy / 300;
        }
        if (counter == 3) { // ~ 30 mile map radius
          dy =  dy / 500;
        }
        dy = -dy + 140;//'-' to reverse N / S map plot
      }


      //draw the newest real-time x,y coord pixel every 4 seconds
      //only draw pixels within map boundaries
      if (((dx > MAP_X_MIN) && (dx < MAP_X_MAX)) && ((dy > MAP_Y_MIN) && (dy < MAP_Y_MAX))) {
        tft.drawPixel( dx, dy, HX8357_WHITE);
      }


      //update and show radians tft X Y ...
      tft.fillRect(60, 200, 40, 55, HX8357_BLACK);
      tft.setCursor(0, 200);
      tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
      tft.print("ZOOM I/O : ");//0-3, 0 is 50m
      tft.println(counter);
      tft.setCursor(0, 215);
      tft.print("RADIANS X: ");
      tft.println(radiansX);
      tft.setCursor(0, 230);
      tft.print("RADIANS y: ");
      tft.println(radiansY);

      //update and show current X Y ...
      tft.fillRect(10, 165, 90, 30, HX8357_BLACK);
      tft.setCursor(0, 165);
      tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
      tft.print("X: ");
      tft.println(dx);
      tft.setCursor(0, 180);
      tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
      tft.print("Y: ");
      tft.println(dy);

      //update and show precision and satellites
      tft.fillRect(60, 45, 40, 10, HX8357_BLACK);
      tft.setCursor(0, 45);
      tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
      tft.print("SATELITES: ");
      tft.println(gps.satellites());
      tft.fillRect(60, 60, 40, 10, HX8357_BLACK);
      tft.setCursor(0, 60);
      tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
      tft.print("PRECISION: ");
      if (gps.hdop() < 1000) {
        tft.println(gps.hdop());
      }
    }
    //ina610();//read sensor every ~4 seconds debugging
    tft.fillRect(35, 270, 40, 15, HX8357_BLACK);
    tft.setCursor(0, 270);
    tft.setTextColor(HX8357_WHITE);  tft.setTextSize(1);
    tft.print("VOLTS: ");
    tft.println(ina260.readBusVoltage() / 1000); //convert mV to V
  }
}

I would store the raw lat and lon in the file. The last point that came in is in the center of the screen. For each data point in the file, subtract the current lat/lon, multiply or divide by the scaling factor, and offset by the center position of the screen. If the point is within the screen boundaries, display it.

I have no idea why there is a conversion to radians. If you want a position in distance units, convert the offsets in degrees (after subtracting the current center lat/lon) to offsets in a distance unit like meters, kilometers, miles... whatever is appropriate for the map scale. The conversion will be a constant for longitude and varies by latitude.

Hi John! Thx for reply. So when I plot the FIRST point from which all other points are plotted in my current program, THAT first point is based on screen center and offset. So maybe doing what you say, I can offset ALL the new points to screen center, and to their difference from the original point! Thx John that could work...

I was also thinking of making a tft width height based array in a function that will do what you suggest....just populating the newest coords into the array, and dropping the last set....but it's a 2 dimensional array and I have not worked much with them....

or just convert the data sets off the SD using your method and only drawing what fits into screen W/H like I do with my current program.....

as far as radians, there are at least three ways to convert decimal degrees to x/y.....some of my formula may be redundant. i will prune it later once I get the 'scrolling map' working... I am not using distance per se, only relative positions of pixels as drawn on my screen....so no miles, meters, etc...

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