/********************************/
/* WO4ROB GPS Home Locator V1_3 */
/* Created: 30 Jul 2021 */
/* Updated: 05 Dec 2021 */
/********************************/
// This is a GPS home locator
// that reads GPS data from a BN-220 GPS module, and
// displays the information on a SSD1306 OLED module (128x64, I2C, 0x3C),
// The thermometer is a Thermistor 3950, 100k, NTC
// one end goes to 5v, the other end goes to a 100k resistor
// the other end of the resistor goes to ground
// the junction between the thermistor and resistor connects to any analog pin
// (pin 2, 3, or 4 on an AtTiny, pin 14-19 on a NANO)
// BN-180 GPS NANO
// +---------+
// \ VCC ---- 5V
// / GND ---- GND
// \ RXD ---- D5 (Grn)
// / TXD ---- D4 (Wht)
// +---------+
// OLED NANO
// +---------+
// \ GND --- GND
// / VCC --- 5V
// \ SCL --- A5
// / SDA --- A4
// +---------+
// Libraries //
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <U8x8lib.h> //
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);
#include <EEPROM.h>
#include <TimeLib.h>
// Constants //
TinyGPSPlus gps; // TinyGPS++ object
SoftwareSerial ss(4,5); // SoftwareSerial (4 TX wht, 5 RX grn) connection to the GPS module
const byte ModeButton = 2; // The other side of the switch is coonected to GND
const byte SaveButton = 12; // The other side of the switch is coonected to GND
const byte ThermistorPin = A0; // Analog pin connected to thermistor and resistor junction
const byte BatteryPin = A6; // Analog pin connected to the battery resistor junction
const float R1 = 10000; // Adjust this value for temperature acuracy.
// 10000 is the default. Reducing it makes the temperature go up.
// 10000 = 73', 9500 = 76', 9000 = 78'
// Steinhart-Hart Coefficient Values
const float c1 = 1.009249522e-03, c2 = 2.378405444e-04, c3 = 2.019202697e-07;
// Variables //
byte Mode = 1, o1, o2, o3, a1, a2, a3;
float remainder, HomeLat, HomeLon, HomeAlt; // 34.198513, -116.282432, 2750.00
char CallSign[7]; // WO4ROB plus termination byte = 7 bytes
boolean LastModeButton = HIGH;
boolean CurrentModeButton = HIGH;
int Vo; // Thermistor Voltage out
float R2; // Thermistor
float logR2;
float Tk; // Temperature in Kelvin
float Tc; // Temperature in Celsius
float Tf; // Temperature in Fahrenheit
int offsetHour; // -7 is USA Pacific Standard Time (PST)
void setup()
{
ss.begin(9600); // Start SoftwareSerial at 9600 baud
pinMode(ModeButton, INPUT_PULLUP); // Uses internal pullup resister
pinMode(SaveButton, INPUT_PULLUP); // Uses internal pullup resister
// Put data in EEPROM (Comment the following 6 lines after the 1st initiation time)
//EEPROM.put(1, 34.198513); // Home Latitude
//EEPROM.put(6, -116.282432); // Home Longitude
//EEPROM.put(11, 1); // Mode
//EEPROM.put(13, "WO4ROB"); // My Callsign
//EEPROM.put(20, 2750.00); // Home Altitude
//EEPROM.put(25, -7); // Local time offset Hour
// Get data from Electrically Erasable Programmable Read-Only Memory (EEPROM)
EEPROM.get( 1, HomeLat); // Get current latitude from memory
EEPROM.get( 6, HomeLon); // Get current longitude from memory
EEPROM.get(11, Mode); // Get saved Mode from memory
EEPROM.get(20, HomeAlt); // Get current altitude from memory
EEPROM.get(13, CallSign);
EEPROM.get(25, offsetHour);
// Display the Splash Screen
u8x8.begin(); // Start the OLED display
u8x8.clear(); // Clear the screen
u8x8.setContrast(255); // Set brightness 0 - 255
LrgFont();
u8x8.setCursor(0,1); // Col, Row
u8x8.print("WO4ROB");
MedFont();
u8x8.setCursor(12,10); // Col, Row
u8x8.print(".com");
u8x8.drawString(0,5,"GPS Home Locator"); // col, row
delay(3000); u8x8.clear(); // 3000 = 3 seconds
} // End of setup
void loop()
{
// Get information from the GPS satellites
unsigned long start = millis();
do {while (ss.available()) gps.encode(ss.read());}
while (millis() - start < 1000); // 1000 = 1 second
// Check Mode Button
if (digitalRead(ModeButton)==1) // If mode button pressed
{
u8x8.clear(); // Clear the screen
Mode = Mode + 1; // Increment the mode
if (Mode > 9) Mode = 1; // There are only 9 modes
EEPROM.put(11, Mode); // Save the Mode to memory
delay(10); // to prevent switch bouncing
}
switch (Mode) // Display GPS information
{
case 1:
// Display Home Navigation
u8x8.setCursor(0,0); // Col, Row
u8x8.print("Home Navigation");
u8x8.setCursor(2,2); // Col, Row
u8x8.print(TinyGPSPlus::distanceBetween(gps.location.lat(), gps.location.lng(), HomeLat, HomeLon)/1000/1.609,2);
u8x8.print(" miles ");
u8x8.setCursor(4,4); // Col, Row
u8x8.print(TinyGPSPlus::courseTo(gps.location.lat(), gps.location.lng(), HomeLat, HomeLon),0);
u8x8.print("* ");
u8x8.print(TinyGPSPlus::cardinal(TinyGPSPlus::courseTo(gps.location.lat(), gps.location.lng(), HomeLat, HomeLon)));
u8x8.print(" ");
u8x8.setCursor(4,6); // Col, Row
u8x8.print(gps.course.deg(),0);
u8x8.print("* ");
u8x8.print(TinyGPSPlus::cardinal(gps.course.deg()));
u8x8.print(" ");
break;
case 2:
// Display Current Location
//MedFont();
u8x8.setCursor(0,0); // Col, Row
u8x8.print("Current Location");
u8x8.setCursor(0,2); // Col, Row
u8x8.print("LAT:");
u8x8.println(gps.location.lat(), 6);
u8x8.print("LON:");
u8x8.println(gps.location.lng(), 6);
u8x8.print("Alt: ");
u8x8.print(gps.altitude.feet(), 0);
u8x8.println(" ft");
if (digitalRead(SaveButton)==0) // If Save Button pressed
{
// Save new home location
HomeLat = gps.location.lat();
HomeLon = gps.location.lng();
HomeAlt = gps.altitude.feet();
// Put data in EEPROM
EEPROM.put( 1, HomeLat); // Save current latitude to memory
EEPROM.put( 6, HomeLon); // Save current longitude to memory
EEPROM.put(20, HomeAlt); // Save current altitude to memory
// Display message
u8x8.clear();
u8x8.setCursor(0,1); // Col, Row
u8x8.println("The current");
u8x8.println("location was");
u8x8.println("saved as home");
delay(3000); // 1000 = one second
u8x8.clear(); //Clear the screen
}
break;
case 3:
// Display Home Location
//MedFont();
u8x8.setCursor(0,0); // Col, Row
u8x8.print("Home Location");
u8x8.setCursor(0,2); // Col, Row
u8x8.print("LAT:");
u8x8.println(HomeLat, 6);
u8x8.print("LON:");
u8x8.println(HomeLon, 6);
u8x8.print("Alt: ");
u8x8.print(HomeAlt, 0);
u8x8.print(" ft");
break;
case 4:
// Display number of sattellites
//MedFont();
u8x8.setCursor(0,1); // Col, Row
u8x8.println(" Communicating");
u8x8.print(" with ");
u8x8.println(gps.satellites.value());
u8x8.print(" satellites.");
break;
case 5:
// Display UTC Date & Time & Grid
//MedFont();
u8x8.setCursor(1,0); // Col, Row
u8x8.print("Universal Time");
// Display UTC Date
u8x8.setCursor(3,2); // Col, Row
u8x8.print(gps.date.year());
u8x8.print(F("/"));
if (gps.date.month() < 10) u8x8.print(F("0"));
u8x8.print(gps.date.month());
u8x8.print(F("/"));
if (gps.date.day() < 10) u8x8.print(F("0"));
u8x8.print(gps.date.day());
// Display UTC Time
u8x8.setCursor(4,4); // Col, Row
if (gps.time.hour() < 10) u8x8.print(F("0"));
u8x8.print(gps.time.hour());
u8x8.print(F(":"));
if (gps.time.minute() < 10) u8x8.print(F("0"));
u8x8.print(gps.time.minute());
u8x8.print(F(":"));
if (gps.time.second() < 10) u8x8.print(F("0"));
u8x8.println(gps.time.second());
break;
case 6:
// Display Local Date & Time
setTime(gps.time.hour(), gps.time.minute(), gps.time.second(), gps.date.day(), gps.date.month(), gps.date.year());
adjustTime(offsetHour * SECS_PER_HOUR);
//MedFont();
u8x8.setCursor(3,0); // Col, Row
u8x8.print("Local Time");
// Display Local Date
u8x8.setCursor(3,2); // Col, Row
u8x8.print(dayShortStr(weekday()));
u8x8.print(" ");
if(day() < 10) u8x8.print('0');
u8x8.print(day());
u8x8.print(" ");
u8x8.print(monthShortStr(month()));
// Display Local Time
u8x8.setCursor(4,4); // Col, Row
u8x8.print(hour());
u8x8.print(":");
if(minute() < 10) u8x8.print('0');
u8x8.print(minute());
u8x8.print(":");
if(second() < 10) u8x8.print('0');
u8x8.print(second());
// Display offset Hour
u8x8.setCursor(2,6); // Col, Row
u8x8.print("UTC ");
u8x8.print(offsetHour);
u8x8.print(" Hours ");
if (digitalRead(SaveButton)==0) // If Save Button pressed
{
offsetHour = offsetHour + 1; // Increment the UTC offset Hour
if (offsetHour > 12) offsetHour = -12; // There are only 12 time zones
// Put data in EEPROM
EEPROM.put(25, offsetHour); // Save current offset Hour to memory
}
break;
case 7:
// Display current Speed
LrgFont();
u8x8.setCursor(3,0); // Col, Row
if (gps.speed.kmph()<10) u8x8.print("0");
u8x8.print(gps.speed.kmph(),2);
MedFont();
u8x8.setCursor(0,4); // Col, Row
u8x8.print(gps.altitude.feet(),0);
u8x8.print("' ");
u8x8.print(gps.course.deg(),0);
u8x8.print("* ");
u8x8.print(TinyGPSPlus::cardinal(gps.course.deg()));
u8x8.print(" ");
break;
case 8:
// Display Temperature
// Calculate temperature
Vo = analogRead(ThermistorPin); // 0-1023, 470 = 70 degrees fahrenheit
R2 = R1 * (1023.0 / (float)Vo - 1.0);
logR2 = log(R2);
Tk = (1.0 / (c1 + c2*logR2 + c3*logR2*logR2*logR2)); // Kelvin
Tc = Tk - 273.15; // Celsius
Tf = (Tc * 9.0)/ 5.0 + 32.0; // Fahrenheit
// Display Temperature
u8x8.setCursor(2,0); // Col, Row
u8x8.print("Temperature");
LrgFont();
u8x8.setCursor(3,2); // Col, Row
u8x8.print(Tc,0);
u8x8.print("'C ");
MedFont();
u8x8.setCursor(2,6); // Col, Row
u8x8.print(Tk,0);
u8x8.print("'K ");
u8x8.print(Tf,0);
u8x8.print("'F ");
break;
case 9:
// Display Battery Level
u8x8.setCursor(1,0); // Col, Row
u8x8.print("Battery Level");
u8x8.setCursor(3,2); // Col, Row
float involts = 0.0, readval = 0.0;
// Read the voltage value at the analog pin
// 5.0 = the max reference voltage
// 1024 = 10 bit analog to digital converter (ADC) (0-1023)
readval = ((analogRead(BatteryPin) * 5.0) / 1024);
// Calculate the input / battery voltage
// 1026000.00 = two 1 meg ohm resistors
involts = readval / (1026000.00/(1026000.00+1026000.00));
u8x8.print(involts);
u8x8.print(" volts");
u8x8.print("");
u8x8.setCursor(1,4); // Col, Row
u8x8.println("Change if under");
u8x8.setCursor(3,6); // Col, Row
u8x8.print("6.50 volts");
break;
default:
// Default is optional
break;
} // End of switch
} // End of loop
// FUNCTIONS //
void GridSquare()
{
// Longitude
remainder = gps.location.lng() + 180;
o1 = (byte)(remainder / 20);
remainder = remainder - (float) o1 * 20;
o2 = (byte)(remainder / 2);
remainder = remainder - 2 * (float) o2;
o3 = (byte)(12 * remainder);
// Latitude
remainder = gps.location.lat() + 90;
a1 = (byte)(remainder / 10);
remainder = remainder - (float) a1 * 10;
a2 = (byte)(remainder);
remainder = remainder - (float) a2;
a3 = (byte)(24 * remainder);
u8x8.setCursor(2,6); // Col, Row
u8x8.print("Grid: ");
u8x8.print((char)(o1 + 'A'));
u8x8.print((char)(a1 + 'A'));
u8x8.print((char)(o2 + '0'));
u8x8.print((char)(a2 + '0'));
u8x8.print((char)(o3 + 'a'));
u8x8.print((char)(a3 + 'a'));
}
void LrgFont()
{
u8x8.setFont(u8x8_font_inb21_2x4_r); // Large font
}
void MedFont()
{
u8x8.setFont(u8x8_font_8x13_1x2_f);//*Medium square
}