XRAD'S Teensy 3.6 read write to SD x/y integer coord pairs

XRAD

Well-known member
XRAD'S Teensy 3.6 How to read write to SD x/y integer coord pairs?

Hello,

I have a GPS point set which is converted to x/y points (integers) for tft pixel display. I have the display points showing up just fine for a walk around the area within the bounds of my TFT 'map.' But when I want to scale the pixel points to a larger area (zoom in or zoom out), I will need to redraw all the current points and display the new 'map' of pixels at selected 'scale.'

I 'think' that I have to save the points to SD in order to redraw them (otherwise, I will only have the currently calculated x/y point pair to draw)

However! I am trying to save an 'unknown' quantity of x/y integer pair points to the SD (unknown because I may walk for 1 minute, or one hour, and the points set may need to be stored via an adaptive array (vector array?). Then, if I scale the map up or down, all the points saved would be redrawn to a TFT at the chosen scale (basically zoom in/zoom out )

1) So...is this the best strategy?

2) How do I save a dynamic/variable sized array of 'pairs'?

2) and how do I recall them in 'pairs' and display them all to the TFT as a group?

Thank you for any tips!! Pic of a live display of plotted points at base scale.... (accuracy meters to pixels is NOT required)

here is my code so far:
Code:
#include <SD.h>
#include <TinyGPS.h>
#include <Adafruit_GFX.h>
#include "Adafruit_ILI9341.h"

#define TFT_CS    8      // TFT CS  pin is connected to arduino pin 8
#define TFT_RST   9      // TFT RST pin is connected to arduino pin 9
#define TFT_DC    10     // TFT DC  pin is connected to arduino pin 10

// initialize ILI9341 TFT library
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

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 long homeX;
int long homeY;

int centeredX;
int centeredY;

int dx;
int dy;

//not used yet
#define MAP_WIDTH 210
#define MAP_HEIGHT 240
#define MAP_CENTERX 215
#define MAP_CENTERY 120
#define width 210
#define height 240


File myFile;

/*
  void drawCross() {//draw a solid cross
  tft.drawFastVLine(215, 0, 240, ILI9341_GREEN);
  tft.drawFastHLine(110, 120, 210, ILI9341_GREEN);
  }
*/

void drawMap() { //draw a map w/dotted cross
  tft.drawRoundRect(110, 0, 210, 240, 10, ILI9341_GREEN);
  for (int x = 55; x < 160; x++) {
    tft.drawPixel((2 * x), 120, ILI9341_GREEN);
  }
  for (int y = 0; y < 120; y++) {
    tft.drawPixel(215, (2 * y), ILI9341_GREEN);
  }
  tft.setCursor(210, 2);
  tft.fillRect(210, 2, 10, 14, ILI9341_BLACK);
  tft.setTextColor(ILI9341_GREEN);  tft.setTextSize(2);
  tft.println("N");
}


void setup()
{
  Serial.begin(115200);
  Serial1.begin(9600);//my GPS device uses 9600 baud, using teensy Serial1

  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");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");

  myFile = SD.open("test.txt", FILE_WRITE);

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

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

    // read from the file until there's nothing else in it:
    while (myFile.available()) {
      Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }

  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(40, 100);
  tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(3);
  tft.println("GPS ACTIVATED");

  delay(2000);
  tft.fillScreen(ILI9341_BLACK);

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

  drawMap();

  startMillis = millis();
  delay(5000);//allow GPS power up
}



void loop()
{
  bool newData = false;
  unsigned long chars;
  unsigned short sentences, failed;

  // For one second we parse GPS data and report some key values
  for (unsigned long start = millis(); millis() - start < 1000;)
  {
    while (Serial1.available())
    {
      char c = Serial1.read();
      // Serial.write(c); // uncomment this line if you want to see the GPS data flowing
      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);
    //Serial.print("LAT=");
    //Serial.print(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flat, 4);
    //Serial.print(" LON=");
    //Serial.print(flon == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flon, 4);
    //Serial.print(" SAT=");
    //Serial.print(gps.satellites() == TinyGPS::GPS_INVALID_SATELLITES ? 0 : gps.satellites());
    //Serial.print(" PREC=");
    //Serial.println(gps.hdop() == TinyGPS::GPS_INVALID_HDOP ? 0 : gps.hdop());

    tft.fillRect(20, 15, 90, 10, ILI9341_BLACK);
    tft.setCursor(0, 15);
    tft.setTextColor(ILI9341_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, ILI9341_BLACK);
    tft.setCursor(0, 30);
    tft.setTextColor(ILI9341_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(ILI9341_WHITE);  tft.setTextSize(1);
      tft.print("LAT: ");
      tft.println(flat, 6);
      tft.setCursor(0, 120);
      tft.print("LON: ");
      tft.println(flon, 6);

      float radiansX = ( flat * (asin(1)) / 90 );
      float radiansY = ( flon * (asin(1)) / 90 );
      //Serial.print("radians x:  ");
      //Serial.println(radiansX);
      //Serial.print("radians y:  ");
      //Serial.println(radiansY);

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

      //Serial.print("homex:  ");
      //Serial.println(homex);
      //Serial.print("homey:  ");
      //Serial.println(homey);

      //tft.drawPixel( homex, homey, ILI9341_WHITE);//draw home pixel
      oneTimePosition = false;
    }

    float radiansX = ( flat * (asin(1)) / 90 );
    float radiansY = ( flon * (asin(1)) / 90 );

    //Serial.print("radians new x:  ");
    //Serial.println(radiansX);
    //Serial.print("radians new y:  ");
    //Serial.println(radiansY);

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


    if (homex != x) {
      dx = (x - homex);
      dx = dx + 215;
    }

    if (homey != y) {
      dy = (y - homey)  ;
      dy = -dy + 120;//reverse N/S here
    }

      //record x/y data pairs to SD here?

    //draw the newest x,y coord pixel every second
    tft.drawPixel( dx, dy, ILI9341_WHITE);

    Serial.print("x coord pixel:  ");
    Serial.println(dx);
    Serial.print("y coord pixel:  ");
    Serial.println(dy);
    Serial.println("");

    //clear radians tft X Y ...
    tft.fillRect(60, 200, 40, 40, ILI9341_BLACK);
    tft.setCursor(0, 200);
    tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(1);
    tft.print("RADIANS X: ");
    tft.println(radiansX);
    tft.setCursor(0, 220);
    tft.print("RADIANS Y: ");
    tft.println(radiansY);

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

    //update and show precision and satellites
    tft.fillRect(60, 45, 40, 10, ILI9341_BLACK);
    tft.setCursor(0, 45);
    tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(1);
    tft.print("SATELITES: ");
    tft.println(gps.satellites());
    tft.fillRect(60, 60, 40, 10, ILI9341_BLACK);
    tft.setCursor(0, 60);
    tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(1);
    tft.print("PRECISION: ");
    tft.println(gps.hdop());
  }
}
 

Attachments

  • IMG_0711.JPG
    IMG_0711.JPG
    131.6 KB · Views: 27
Last edited:
so I can save x,y integers to SD now and it looks like this in the SD file (and read them back as a batch to Serial.print) :

221,120
234,123
etc,,,

or I can save x,y to SD like this:

x=221,y=120
x=234,y=123
etc...

What is the best strategy to read back these lines of data in order to
draw ALL the x,y coords from SD?

tft.drawPixel( x, y, ILI9341_WHITE);
 
Nice job. My preference is just to write the values:
221,120
234,123
etc,,,

Your first line in the file could always be
x, y
just for reference
 
Nice job. My preference is just to write the values:
221,120
234,123
etc,,,

Your first line in the file could always be
x, y
just for reference


Thank you mjs513! I can add to the SD file each read of the GPS (1 read per second). And I can read the complete SD file. But now I have to get all these lines from the SD myfile and draw pixels with them. But I have no idea how...

should I try to save the entire newest set of SD data lines into chip memory first and then draw them? into a buffer of size determined by # of bytes in SD file? or just set buffer as large as possible?

should I try to read and draw the pixels straight from SD?

And how do I do either or both of these?

I think that for quickly drawing pixels, I would want the least number of calls to drawPixel...but not sure how to do this..

I am trying to think of this as drawing an image from the SD to the tft...


for( i = 0; i <sizeof (myfile)-1 ; i++){//which I know doesn't work

read each line of myfile

then for each line in file
do this : tft.drawPixel( x, y, ILI9341_WHITE);

until no more lines to read
}

can anyone point me in the right direction ? Thank you
 
Last edited:
I can store my D.D. GPS converted to x/y integers to SD that look like this:

214,123
215,122
etc...

I can draw them at original scale (of approx 1 pix per m). Or I can scale them with several scales (x/100,x/500.etc..) and draw the new scale, but I am not able to redraw the saved x/y SD pairs in entirety, only the newest SD pixel values..

When I rescale to pixel size to say 1/100...I need to clear the tft, and then redraw all the pixels from the SD at the new scale so the tft is repopulated with all pixel points at correct scale.

the SD file is not of a fixed length as every second from start of loop(), a new x/y pair is added, but the whole file will need to be utilized to create a new set of pixels on the tft if scale is changed...

so maybe I need to read()SD file and as I am reading each line, convert coord pair to new scale, and then draw tft pixel....

Does anyone have any function for retrieving all the SD comma separated line pairs one by one from beginning to end? ( in the Arduino SD notes, read file reads whole file, and that is fine on serial monitor, but I can't seem to get the SD data one line at a time into my loop functions....)

Am I being clear or unclear?

Any help appreciated!
 
Last edited:
In case anyone interested, I solved this data store and rewrite to TFT in the code below. I seem to have two remaining issues:

1) my north - south coordinates produce a slanted line (northeast to southwest..off by about 45 degrees), rather than true north south, while east west is just fine

2) I would like to find a way to CENTER the LAST set of saved to SD map coordinates (ie: the most recent), draw the older coordinates in relation to the last centered set, and continue my journey from there, upon boot.

anyone have any ideas?


Code:
 /*  XRAD'S REALLY COOL GPS TRACKER 1/28/22 using adafruit 3.5 touch
  tft breakout, adafruit STMPE610, Teensy 3.6 w/SD. I updated
  the GPS unit to a UBlox read with tinyGPSPLUS. 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 could 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 INA260 power sensor
  Acxico 1Pcs NEO-8M UBlox GPS Satellite Positioning Module (sees 11 sats indoors!)
  uxcell GPS Active Antenna 90-Degree SMA 28dB Aerial 0.1 Meter (I removed magnet)
  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...


  new tinyGPSPlus (tinyGPS++) functions:

  Serial.println(gps.location.lat(), 6); // Latitude in degrees (double)
  Serial.println(gps.location.lng(), 6); // Longitude in degrees (double)
  Serial.print(gps.location.rawLat().negative ? "-" : "+");
  Serial.println(gps.location.rawLat().deg); // Raw latitude in whole degrees
  Serial.println(gps.location.rawLat().billionths);// ... and billionths (u16/u32)
  Serial.print(gps.location.rawLng().negative ? "-" : "+");
  Serial.println(gps.location.rawLng().deg); // Raw longitude in whole degrees
  Serial.println(gps.location.rawLng().billionths);// ... and billionths (u16/u32)
  Serial.println(gps.date.value()); // Raw date in DDMMYY format (u32)
  Serial.println(gps.date.year()); // Year (2000+) (u16)
  Serial.println(gps.date.month()); // Month (1-12) (u8)
  Serial.println(gps.date.day()); // Day (1-31) (u8)
  Serial.println(gps.time.value()); // Raw time in H H M M S S C C format (u32)
  Serial.println(gps.time.hour()); // Hour (0-23) (u8)
  Serial.println(gps.time.minute()); // Minute (0-59) (u8)
  Serial.println(gps.time.second()); // Second (0-59) (u8)
  Serial.println(gps.time.centisecond()); // 100ths of a second (0-99) (u8)
  Serial.println(gps.speed.value()); // Raw speed in 100ths of a knot (i32)
  Serial.println(gps.speed.knots()); // Speed in knots (double)
  Serial.println(gps.speed.mph()); // Speed in miles per hour (double)
  Serial.println(gps.speed.mps()); // Speed in meters per second (double)
  Serial.println(gps.speed.kmph()); // Speed in kilometers per hour (double)
  Serial.println(gps.course.value()); // Raw course in 100ths of a degree (i32)
  Serial.println(gps.course.deg()); // Course in degrees (double)
  Serial.println(gps.altitude.value()); // Raw altitude in centimeters (i32)
  Serial.println(gps.altitude.meters()); // Altitude in meters (double)
  Serial.println(gps.altitude.miles()); // Altitude in miles (double)
  Serial.println(gps.altitude.kilometers()); // Altitude in kilometers (double)
  Serial.println(gps.altitude.feet()); // Altitude in feet (double)
  Serial.println(gps.satellites.value()); // Number of satellites in use (u32)
  Serial.println(gps.hdop.value()); // Horizontal Dim. of Precision (100ths-i32)
*/

#include <SD.h>
#include <TinyGPSPlus.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


TinyGPSPlus gps;



bool  oneTimePosition = true;
int long oneTimeLat, oneTimeLong;

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

#define  R 6378137 // 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;//for scaling draw pixels to map size 0-3
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 = "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;
        delay(1000);
        msg = "PRESS SEL";
        //Serial.println(msg);
        setTextColorIndex(0);
        tftCenterPrint(msg);
        break;

      case BTN_SELECT:
        if (clearSDMap == false) {
          msg = "SAVE SD 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;
        delay(1000);
        msg = "PRESS SEL";
        //Serial.println(msg);
        setTextColorIndex(0);
        tftCenterPrint(msg);
        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 = "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 = "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, 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: ");

  tft.setTextColor(HX8357_WHITE);
  tft.setCursor(0, 15);
  tft.print("LAT: ");
  tft.setCursor(0, 30);
  tft.print("LON: ");

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

  tft.setCursor(0, 105);
  tft.print("LAT: ");
  tft.setCursor(0, 120);
  tft.print("LON: ");

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

  tft.setCursor(0, 200);
  tft.print("ZOOM I/O : ");
  tft.setCursor(0, 215);
  tft.print("SPEED MPH: ");
  tft.setCursor(0, 230);
  tft.print("ALTIM FT: ");

  tft.setCursor(0, 295);
  tft.setTextSize(2);
  tft.print("INFO: ");
}


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 / 50;
          SDdx =  SDdx + 250;
        }
        else if (counter == 2) { // ~ 15 mile map radius
          SDdx =  SDdx / 150;
          SDdx =  SDdx + 250;
        }
        else if (counter == 3) { // ~ 30 mile map radius
          SDdx =  SDdx / 300;
          SDdx =  SDdx + 250;
        }
        //for scaling: calculate SDdy here
        if (counter == 1) { // ~  5 mile map radius
          SDdy =  SDdy / 50;
          SDdy =  SDdy + 140;//don't reverse N / S map plot, it's already calculated
        }
        else if (counter == 2) { // ~ 15 mile map radius
          SDdy =  SDdy / 150;
          SDdy =  SDdy + 140;
        }
        else if (counter == 3) { // ~ 30 mile map radius
          SDdy =  SDdy / 300;
          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() {
  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...");
  //SD.begin(BUILTIN_SDCARD);
  // 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 ACTIVATING");
  tft.setCursor(70, 150);
  tft.setTextColor(HX8357_WHITE);
  tft.setTextSize(3);
  tft.println("USE MENU ONLY AFTER");
  tft.setCursor(100, 200);
  tft.setTextColor(HX8357_WHITE);
  tft.setTextSize(3);
  tft.println("GPS IS ACTIVE");

  delay(4000);
  tft.fillScreen(HX8357_BLACK);

  drawSetup();
 
  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);

  drawMap();

  //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();//do button stuff here...

  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() > 0)
      {
        gps.encode(Serial1.read()); // Did a new valid sentence come in?
      }
    }

    if (gps.location.isValid()) {

      float flat, flon;

      tft.fillRect(20, 15, 90, 10, HX8357_BLACK);
      tft.setCursor(0, 15);
      tft.setTextColor(HX8357_WHITE);
      tft.setTextSize(1);
      tft.print("LAT: ");
      flat = (gps.location.lat());
      tft.println(gps.location.lat(), 6);
      //Serial.print("LAT:  ");
      //Serial.println(flat, 6);


      tft.fillRect(20, 30, 90, 10, HX8357_BLACK);
      tft.setCursor(0, 30);
      tft.print("LON: ");
      flon = (gps.location.lng());
      tft.println(gps.location.lng(), 6);
      //Serial.print("LON:  ");
      //Serial.println(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(gps.location.lat(), 6);
        tft.setCursor(0, 120);
        tft.print("LON: ");
        tft.println(gps.location.lng(), 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;

        oneTimePosition = false;
      }


      if (dx > 0 && dy > 0) {
        setTextColorIndex(0);
        tftCenterPrint("GPS ACTIVE");
      }

      //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 / 50;
        }
        if (counter == 2) { // ~ 15 mile map radius
          dx =  dx / 150;
        }
        if (counter == 3) { // ~ 30 mile map radius
          dx =  dx / 300;
        }
        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 / 50;
        }
        if (counter == 2) { // ~ 15 mile map radius
          dy =  dy / 150;
        }
        if (counter == 3) { // ~ 30 mile map radius
          dy =  dy / 300;
        }
        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 some data...
      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("SPEED MPH: ");
      tft.println(gps.speed.mph());
      tft.setCursor(0, 230);
      tft.print("ALTIM FT: ");
      tft.println(round(gps.altitude.feet()));

      //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.println(gps.satellites.value());

      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.value()) < 1000) {
        tft.println(gps.hdop.value());
      }
    }
    //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
  }
}
 
here are pics of the device:
 

Attachments

  • my gps 1.JPG
    my gps 1.JPG
    197.7 KB · Views: 18
  • my gps.JPG
    my gps.JPG
    118.3 KB · Views: 21
Back
Top