Top two functions are original from Mikal Hart. I wrote the two that compact the GPS info into 16 bytes and save to EEPROM
void printFloat(double number, int digits)
{
// Handle negative numbers
if (number < 0.0)
{
Serial.print('-');
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for (uint8_t i=0; i<digits; ++i)
rounding /= 10.0;
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long)number;
double remainder = number - (double)int_part;
Serial.print(int_part);
// Print the decimal point, but only if there are digits beyond
if (digits > 0)
Serial.print(".");
// Extract digits from the remainder one at a time
while (digits-- > 0)
{
remainder *= 10.0;
int toPrint = int(remainder);
Serial.print(toPrint);
remainder -= toPrint;
}
}
bool feedgps()
{
while (nss.available())
{
if (gps.encode(nss.read()))
return true;
}
return false;
}
// The code above this line was contributed by Mikal Hart (http://arduiniana.org)
void GPS_to_EEPROM(unsigned long *pointer)
/*
The function assumes that the TinyGPS object gps is already initialized and ready to send data.
It also assumes that the caller feeds the GPS and checks the pointer so the pointed address will not exceed the address space of the EEPROM, or cross page boundaries while writing.
*/
{
double spdf;
unsigned long spd;
long lat, lon, alt;
unsigned long age, dat, tim;
unsigned long buf[4];
gps.get_position(&lat, &lon, &age);
gps.get_datetime(&dat, &tim, &age);
alt=gps.altitude();
spdf=gps.f_speed_mph();
spd=spdf*100;
buf[0]=lat;
buf[1]=lon;
buf[2]=((alt/10)<<16)|((dat/10000)<<11)|(((dat%10000)/100)<<7)|(dat%100); // Altitude only takes 16 bit. It is in the unit of 0.1m instead of 1cm, which is not necessary in accuracy. date(when expressed in binary 5bit date-4bit month-7bit year, takes no more than 16 bit. Combine them together.
buf[3]=(spd<<17)|((tim/1000000)*3600+((tim%1000000)/10000)*60+(tim%10000)/100); // Speed, expressed in 1/100 mph, takes less than 15 bits. Compact hhmmsscc into seconds since 00:00 and lose the 1/100 seconds. This is 17 bit long.
i2c_eeprom_write_page(0x50, (unsigned int) (*pointer), (byte*) buf, 16); // Store 16 bytes of data at location of the pointer.
(*pointer)=(*pointer)+16; // Increment pointer.
delay(5); // Make sure the data is written to the EEPROM.
}
boolean EEPROM_to_GPS(long *lat, long *lon, long *alt, unsigned long *tim, unsigned long *dat, double *spdf, unsigned long *pointer)
/*
This function reads one EEPROM entry, if it's non zero, parse it into long integer forms (double precision for speed), and returns true.
If the EEPROM entry is empty, return false.
To stay isolated from the main program, this function doesn't check if the pointer will be beyond the EEPROM size, which is left to the caller to do.
*/
{
unsigned long buf[4];
i2c_eeprom_read_buffer (0x50, (unsigned int) (*pointer), (byte*) buf, 16);
if ((buf[0]==0)&&(buf[1]==0)&&(buf[2]==0)&&(buf[3]==0)) return false;
*lat=(long)buf[0];
*lon=(long)buf[1];
*dat=buf[2]&0xFFFF;
*dat=(*dat>>11)*10000+((*dat>>7)&15)*100+(*dat&127); //Process data to turn into 12/27/10 form
*alt=10*((long)buf[2]>>16);
*tim=(buf[3]&0x1FFFF);
*tim=(*tim/3600)*1000000+((*tim%3600)/60)*10000+(*tim%60)*100; //Process time to turn into 12:59:0000 form
*spdf=((double)((buf[3])>>17))/100.0;
(*pointer)=(*pointer)+16; // Increment pointer by 16 bytes.
return true;
}
Menus and functions:
void msg_lcd(char* msg_line)
{
char msg_buffer[17];
strcpy_P(msg_buffer,msg_line);
lcd.print(msg_buffer);
}
void render_menu(int me)
{
char menu_buffer[20];
lcd.clear();
msg_lcd(msg_07);
lcd.setCursor(0,1);
strcpy_P(menu_buffer,(char*)pgm_read_word(&(menu_item[me])));
lcd.print(menu_buffer);
}
void do_menu()
{
int temp1;
temp1=hmi_with_update_2(menu_pointer,0,n_menu_items-1,1,16,1,0,render_menu); // Sticky menu item.
menu_pointer=(temp1==-1)?menu_pointer:temp1; // In case escape was triggered, value doesn't change.
switch (temp1)
{
case menu_PC:
_send_to_PC();
break;
case menu_erase:
_erase();
break;
case menu_record:
_record();
break;
case menu_display:
_display();
break;
case menu_para:
_parameters();
break;
}
}
void _record_display()
{
double spdf;
unsigned long spd;
long lat, lon, alt;
unsigned long age, dat, tim;
unsigned long buf[4];
char msg[17];
bool newdata = false;
unsigned long start = millis();
// Every few seconds we print an update
pointer=lower_limit; // Load the lower limit.
while(1)
{
while (millis() - start < period*1000)
{
if (feedgps())
newdata = true;
}
start=millis();
if (newdata) // Update GPS coordinates on LCD
{
gps.get_position(&lat, &lon, &age);
lcd.clear();
sprintf(msg,"Lat:%ld",lat);
lcd.print(msg);
lcd.setCursor(0,1);
sprintf(msg,"Long:%ld",lon);
lcd.print(msg);
if (recording)
{
if (pointer<upper_limit)
{
GPS_to_EEPROM(&pointer);
lcd.setCursor(15,0);
lcd.write(1);
}
else
{
lcd.clear();
lcd.print("Limit reached");
wait_on_escape(2000);
return;
}
}
}
}
}
void _send_to_PC()
{
double spdf;
unsigned long spd;
long lat, lon, alt;
unsigned long age, dat, tim;
unsigned long buf[4];
pointer=0;
if (!verbose) Serial.println("Lat(10^-5 deg)\tLong(10^-5 deg)\tDate(ddmmyy)\tTime(hhmmsscc)\tAlt(cm)\tSpeed(mph)");
while (EEPROM_to_GPS(&lat, &lon, &alt, &tim, &dat, &spdf, &pointer)&&(pointer<=EEPROM_size-16))
{
switch (verbose)
{
case false:
Serial.print(lat); Serial.print("\t"); Serial.print(lon); Serial.print("\t");
Serial.print(dat); Serial.print("\t"); Serial.print(tim); Serial.print("\t");
Serial.print(alt); Serial.print("\t"); printFloat(spdf); Serial.println("");
break;
case true:
Serial.print("Lat/Long(10^-5 deg): "); Serial.print(lat); Serial.print(", "); Serial.print(lon); Serial.println("");
Serial.print("Date(ddmmyy): "); Serial.print(dat); Serial.print(" Time(hhmmsscc): "); Serial.print(tim); Serial.println("");
Serial.print("Alt(cm): "); Serial.print(alt); Serial.print(" Speed(mph): "); printFloat(spdf); Serial.println(""); Serial.println("");
break;
default:
break;
}
}
pointer=0;
}
void _erase()
{
int temp1, temp2;
unsigned long buf[4]={0,0,0,0};
lcd.clear();
lcd.print("Erase all data?");
temp1=0;
temp2=hmi_with_update_3(&temp1, 0, 1, 1, 1, 1, 3, render_YN_in_place); // Asks whether outputs to PC in verbose mode.
temp1=(temp2==-1)?0:temp1; // In case escape was triggered, value doesn't change.
if (temp1)
{
lcd.clear();
lcd.print("Erasing...");
lcd.setCursor(0,1);
lcd.print("Please wait");
for (unsigned long addr=0;addr<EEPROM_size;addr+=16)
{
i2c_eeprom_write_page( 0x50, addr, (byte*) buf, 16);
delay(5);
}
}
}
void _record()
{
recording=true;
_record_display();
}
void _display()
{
recording=false;
_record_display();
}
void _parameters()
{
int temp1, temp2;
lcd.clear(); // Input period
lcd.print("Record Period:");
lcd.setCursor(9,1);
lcd.print("Seconds");
temp1=period;
temp2=hmi_with_update_3(&temp1, 1, 32000, 1, 4, 1, 4, render_number_in_place); // Asks for period between recordings.
period=(temp2==-1)?period:temp1; // In case escape was triggered, value doesn't change.
delay(100); // Input verbose mode
lcd.clear();
lcd.print("Verbose mode:");
temp1=verbose;
temp2=hmi_with_update_3(&temp1, 0, 1, 1, 6, 1, 3, render_YN_in_place); // Asks whether outputs to PC in verbose mode.
verbose=(temp2==-1)?period:temp1; // In case escape was triggered, value doesn't change.
delay(100); // Input lower recording limit
lcd.clear();
lcd.print("Record from:");
lcd.setCursor(0,1);
lcd.print("Entry#");
temp1=(lower_limit/16);
temp2=hmi_with_update_3(&temp1, 0, (EEPROM_size/16), 1, 7, 1, 5, render_number_in_place); // Asks the lower limit of the recording.
lower_limit=(temp2==-1)?lower_limit:temp1*16; // In case escape was triggered, value doesn't change.
delay(100); // Input upper recording limit
lcd.clear();
lcd.print("Record till:");
lcd.setCursor(0,1);
lcd.print("Entry#");
temp1=(int)(upper_limit/16UL);
temp2=hmi_with_update_3(&temp1, 0, (EEPROM_size/16), 1, 7, 1, 5, render_number_in_place); // Asks the upper limit of the recording.
upper_limit=(temp2==-1)?upper_limit:((unsigned long)temp1)*16UL; // In case escape was triggered, value doesn't change.
}
Here is the limit of one post. You can find the rest of the code on my blog.