I am using arduino IDE. I have an SD logger program writing x and y coords to one file on SD. The x/y point pair is converted from GPS Decimal Degrees, and the saved x/y pair is per line with line data separated by a comma. X is always the first number set, then comma, then y. Max line is 8 bytes. All x/y number points written will be greater than zero (0). The plan is to redraw the x/y coords to a tft (thereby redrawing the saved map)from SD or from a buffer of string obtained from the SD file. there is only one SD file containing all the x/y pairs. File size is up to 400,000 bytes (about 10 hours of saved data at 1 x/y pair per second) which has not been an issue.
I can easily plot the realtime GPS converted to x/y data points to TFT and get a cool map of my trail.TFT Map IS NOT to scale, and that is not important. What is import is being able to redraw the x/y points to a cleared TFT map screen, from SD or a buffer. I have spent much time reading on this site and others, but can't seem to figure out the code.
I have no issue seeing these x/y lines on SD, or from a buffer, when written to serial monitor.
What I can't figure out is how to read the SD file line by line, parse the characters by using the comma delimiter, and convert them to x and y int, then write to tft.
any GPS file saved to SD, or from SD to a buffer, EACH line looks like this: 230,123
possible range for x is always 3 digits, possible range for y could be 1-239.
anyone have any thoughts? I will post my code if needed, but how I get my data file from the SD is a simple call to read SD (or buffer...)
EDIT here is my code: in the loop, you can see where a button press call the SD or buffer data, which needs to be parsed and converted to new SDx and new SDy...
#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 dx;
int dy;
//not used yet
#define MAP_WIDTH 210
#define MAP_HEIGHT 240
#define MAP_CENTERX 215
#define MAP_CENTERY 120
//map x/y boundaries
#define MAP_X_MIN 110
#define MAP_X_MAX 320
#define MAP_Y_MIN 0
#define MAP_Y_MAX 240
#define width 210
#define height 240
#define SCALINGBUTTON 5
int counter = 0;
int scaleButtonState = LOW;
File myFile;
//not used
char str[7];
const char s[2] = ",";
char *token;
String buffer;
void clearMap() {
tft.fillRoundRect(110, 0, 210, 240, 10, ILI9341_BLACK);
}
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 drawSetup() {
tft.setCursor(0, 200);
tft.setTextColor(ILI9341_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 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.");
Serial.println("x,y");
SD.remove("GPS");//remove prior GPS SD file
myFile = SD.open("GPS", FILE_WRITE);
//pinMode(scaleButtonPin, INPUT);
pinMode(SCALINGBUTTON, INPUT_PULLDOWN);
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: ");
//clear radians tft X Y ...
tft.fillRect(60, 200, 40, 60, ILI9341_BLACK);
tft.setCursor(0, 200);
tft.setTextColor(ILI9341_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: ");
drawMap();
startMillis = millis();
}
void loop() {
scaleButtonState = digitalRead(SCALINGBUTTON);
if (scaleButtonState == HIGH) {
counter++;
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:
while (myFile.available()) {
//Serial.write(myFile.read());
//while (myFile.available()) {
buffer = myFile.readStringUntil('\n');
Serial.println(buffer); //debugging
/*
this is where I need to parse each line and convert to
int and recreate the x/y positions from SD or buffer
//do some action here with recreated x/y for EACH line parsed
//zoom in or out with new x/y
// if (((SDdx > MAP_X_MIN) && (SDdx < MAP_X_MAX)) && ((SDdy > MAP_Y_MIN) && (SDdy < MAP_Y_MAX))) {
// tft.drawPixel( SDdx, SDdy, ILI9341_WHITE);
*/
}
// close the file:
myFile.close();
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
if (counter > 3) {
counter = 0;
}
tft.fillRect(60, 200, 40, 60, ILI9341_BLACK);
tft.setCursor(0, 200);
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(1);
tft.print("ZOOM I/O : ");//
tft.println(counter);
scaleButtonState = LOW;
}
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();
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, 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 );
homex = R * radiansY * cos(radiansX);
homey = R * radiansX;
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 + 215;
}
if (homey != y) {
dy = (y - homey) ;
dy = -dy + 120;//reverse N/S here
}
myFile = SD.open("GPS", FILE_WRITE);
// if the file opened okay, write to it:
if (myFile) {
//Serial.print("Writing to GPS...");
//myFile.print ("dx=");
//myFile.print (", ");
myFile.print (dx);
myFile.print (", ");
//myFile.print ("dy=");
myFile.println(dy);
//myFile.println(" ");
// close the file:
myFile.close();
// Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
//draw the newest x,y coord pixel every second
//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, ILI9341_WHITE);
}
//clear radians tft X Y ...
tft.fillRect(60, 200, 40, 60, ILI9341_BLACK);
tft.setCursor(0, 200);
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(1);
tft.print("ZOOM I/O : ");
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, 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());
}
}
/*
//for scaling
if (homex != x) {
dx = (x - homex);
//calculate dx/dy here for range
if (counter == 0) { // ~ 50 meter radius
dx = dx;
}
else if (counter == 1) { // ~ 5 mile map radius
dx = dx / 100;
}
else if (counter == 2) { // ~ 15 mile map radius
dx = dx / 300;
}
else if (counter == 3) { // ~ 30 mile map radius
dx = dx / 500;
}
dx = dx + 215;
}
if (homey != y) {
dy = (y - homey);
//calculate dx/dy here for range
if (counter == 0) { // ~ 50 meter radius
dy = dy;
}
else if (counter == 1) { // ~ 5 mile map radius
dy = dy / 100;
}
else if (counter == 2) { // ~ 15 mile map radius
dy = dy / 300;
}
else if (counter == 3) { // ~ 30 mile map radius
dy = dy / 500;
}
dy = -dy + 120;//'-' to reverse N / S map plot
}
*/