I have a couple of new Arduino code sketches for GPS. The problem with my previous code was that it repeatedly read the GPS time, and set the RTC. This took too long and so the time display of the seconds jumped to every two seconds!
So I have completely re-written the code, it now waits for the GPS "fix", programs the RTC, gets the lat & Lon and calculates the Maidenhead Locator, then displays these, but displays the time from the programmed RTC, which then goes by seconds. Phew!!
My header file "Oled_128X64_I2C.h" has been published previously, look back and you will find it.
Just to remind you I use a VK-163 GPS (look on Amazon or ebay) which connects by a 4-way 3.5mm jack, and provides NMEA GPS data at 9600 baud. The block digram of the "VFO" I use is below, it uses an Arduino UNO with an AD9851 synthesiser and MMIC output amplifier giving up to 50mW output. The Arduino also drives an OLED 128x64 pixel display, using a U8g2lib library (look up on GitHUb). This is a great box of tricks and I can use it for many different sketches from GPS to WSPR, JT65, RTTY, PSK31, QRSS and DFCW...
Download here
CODE LAT & LON DISPLAY
// GPS_DATE_TIME_LATLON
// V1.0 22-8-17 after fix, set RTC, then display RTC Time & Date
// jack
// TIP +5V
// ring 13 (RX LOW, FROMGPS)
// ring 12 (TX LOW, TOGPS)
// GND
// HEADRERS & LIBRARIES
#include "Oled_128X64_I2C.h"
#include "SoftwareSerial.h"
// CONNECTIONS
#define FROMGPS 13
#define TOGPS 12
#define SW 4
// I2C COMMS DATA
// RTC address
#define RTCADDR 0x68
// OBJECTS
SoftwareSerial gps(FROMGPS, TOGPS);
// GLOBAL VARIABLES
char gpsbuf[200]; // GPS input buffer
char tm[20]; // time HHMMSS
char fix[5]; // fix A|V, init void
char dt[20]; // date YYMMDD
char la[15]; // latitude
char ns[2]; // NS
char lo[15]; // longitude
char ew[2]; // EW
// data converted to numeric (decimal) bytes
byte hrs, mns, sec;
byte yr, mth, dy;
byte dow;
double lat, lon;
// Maidenhead Locator
char mh[10] = "";
// SETUP
void setup() {
// pin modes
pinMode(FROMGPS, INPUT);
pinMode(TOGPS, OUTPUT);
pinMode(SW, INPUT_PULLUP);
// OLED init, I2C addr 0x3C
oled.begin();
// GPS serial init
gps.begin(9600);
strcpy(fix, "V"); // init no fix
}
// LOOP
void loop() {
dispUpdate(); // init display AWTG GPS
do { // read sentence until fix confirmed
getGPS("$GPRMC", gpsbuf); // get $GPRMC
fieldsGPS(gpsbuf); // dig out ASCII fields
} while (strcmp(fix, "A") != 0);
convert(); // convert Time & Date to numeric values
dow = calcDow(yr, mth, dy); // calulate day of week
setRTC(); // program RTC with GPS time
lat = convertPos(la, ns); // convert Lat & Lon to numeric
lon = convertPos(lo, ew);
findMH(mh, lat, lon); // find Maidenhead Locator
while (!button()) { // update Time & Date display from RTC
readRTC(); // read RTC
dispUpdate();
}
strcpy(fix, "V"); // button pressed restart
}
// button pressed?
bool button() {
if (digitalRead(SW) == LOW) { // button pressed?
while (!digitalRead(SW)); // wait for release
return true;
}
else {
return false;
}
}
// GPS FUNCTIONS
// get sentence into buffer, for example
// $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
void getGPS(char *sntc, char *buf) {
do { // find sntc
getline(buf);
} while (strncmp(buf, sntc, 6) != 0);
}
// get a line from the GPS, inc /r/n, add /0
void getline(char *line) {
char c;
int p;
p = 0; // buffer pointer
do {
if (gps.available() > 0) { // data?
c = gps.read(); // read character
line[p++] = c; // put in buffer
}
} while ( c != '\n' ); // stop on /n
line[p] = '\0'; // terminate string
}
// find GPS fields
void fieldsGPS(char *buf) {
// extract strings from $GPRMC fields
xtract(buf, 1, tm); // time HHMMSS
xtract(buf, 2, fix); // fix A or V
xtract(buf, 9, dt); // date YYMMDD
xtract(buf, 3, la); // latitude
xtract(buf, 4, ns); // NS
xtract(buf, 5, lo); // longitude
xtract(buf, 6, ew); // EW
}
// extract field and return string in outbuf
void xtract(char *in, int field, char *out) {
int ip = 0; // input buffer pointer
int op = 0; // output buffer pointer
int f = 0; // field counter
while (f < field) { // find start of field, ip
while (in[ip++] != ',');
f++;
}
while (in[ip] != ',') { // scan to next ','
out[op++] = in[ip++]; // copy in to out
}
out[op] = '\0'; // terminate out string
}
// convert GPS ASCII Time and Date to decimal bytes
void convert() {
hrs = strtob(tm, 0); // HH....
mns = strtob(tm, 2); // ..MM..
sec = strtob(tm, 4); // ....SS
dy = strtob(dt, 0); // DD....
mth = strtob(dt, 2); // ..MM..
yr = strtob(dt, 4); // ....YY
}
// convert ASCII field starting at field pointer, to byte
byte strtob(char *field, int fp) {
char out[20];
strncpy(out, field + fp, 2); // copy 2 char
return (byte)atoi(out); // return byte
}
// convert Lat, Lon strings to decimal +/-NS|EW
double convertPos(char *pos, char *d) {
double pp, mm, ans;
int dd;
pp = atof(pos); // get in decimal ddmm.mmmmmmm
dd = (int)pp / 100; // get degrees part
mm = pp - (100 * dd); // get minutes
ans = dd + (double)mm / 60.0; // calc decimal degrees
if (strcmp(d, "N") == 0 || strcmp(d, "E") == 0) // if positive
return ans;
else
return - ans; // negative
}
// DATE OF WEEK CALC
// calc dow, return decimal byte, Sun = 0
byte calcDow(byte year, byte month, byte day)
{
unsigned long days;
unsigned int febs;
unsigned int months[] =
{
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 // days until 1st of month
};
days = year * 365; // days up to year
febs = year;
if (month > 2) febs++; // number of completed Februaries
// add in the leap days
days += ((febs + 3) / 4);
days -= ((febs + 99) / 100);
days += ((febs + 399) / 400);
days += months[month - 1] + day;
return (byte)(((days + 5) % 7)); // sun = 0
}
// FIND MAIDENHEAD LOCATOR
void findMH(char *dst, double fa, double fo) {
int a1, a2, a3;
int o1, o2, o3;
double rd;
// Latitude
rd = fa + 90.0;
a1 = (int)(rd / 10.0);
rd = rd - (double)a1 * 10.0;
a2 = (int)(rd);
rd = rd - (double)a2;
a3 = (int)(24.0 * rd);
// Longitude
rd = fo + 180.0;
o1 = (int)(rd / 20.0);
rd = rd - (double)o1 * 20.0;
o2 = (int)(rd / 2.0);
rd = rd - 2.0 * (double)o2;
o3 = (int)(12.0 * rd);
dst[0] = (char)o1 + 'A';
dst[1] = (char)a1 + 'A';
dst[2] = (char)o2 + '0';
dst[3] = (char)a2 + '0';
dst[4] = (char)o3 + 'A';
dst[5] = (char)a3 + 'A';
dst[6] = '\0';
}
// SET RTC
// set date and time bytes to RTC BCD
void setRTC() {
// program RTC
Wire.beginTransmission(RTCADDR);
Wire.write(0); // next input at sec register
Wire.write(decToBcd(sec)); // set seconds
Wire.write(decToBcd(mns)); // set minutes
Wire.write(decToBcd(hrs)); // set hours
Wire.write(decToBcd(dow)); // set day of week
Wire.write(decToBcd(dy)); // set date (1 to 31)
Wire.write(decToBcd(mth)); // set month (1-12)
Wire.write(decToBcd(yr)); // set year (0 to 99)
Wire.endTransmission();
}
// Convert decimal to BCD
byte decToBcd(byte dec)
{
return ( (dec / 10 * 16) + (dec % 10) );
}
// READ RTC
void readRTC() {
// Reset the RTC register pointer
Wire.beginTransmission(RTCADDR);
Wire.write(0x00);
Wire.endTransmission();
// request 7 bytes from the RTC address
Wire.requestFrom(RTCADDR, 7);
// get the time date
sec = bcdToDec(Wire.read()); // 0 - 59
mns = bcdToDec(Wire.read()); // 0 - 59
hrs = bcdToDec(Wire.read() & 0b111111); // mask 12/24 bit
dow = bcdToDec(Wire.read()); // 0 = Sunday
dy = bcdToDec(Wire.read()); // 1 - 31
mth = bcdToDec(Wire.read()); // 0 = jan
yr = bcdToDec(Wire.read()); // ..yy
}
// Convert BCD to decimal numbers
byte bcdToDec(byte val) {
return ( (val / 16 * 10) + (val % 16) );
}
// PICTURE LOOP
// this version displays lat/lon
void dispUpdate() {
oled.firstPage();
do {
dispMsg(30, 0, "GPS Lat/Lon");
if (strcmp(fix, "A") != 0) { // no fix?
dispMsgL(30, 15, "AWTG GPS");
}
else {
dispNum(10, 15, lat, 2);
dispMsg(50, 15, ns);
dispNum(70, 15, lon, 2);
dispMsg(110, 15, ew);
dispDate(15, 32, dow, dy, mth, yr);
dispTimeL(25, 47, hrs, mns, sec);
}
} while ( oled.nextPage() );
}
CODE MAIDENHEAD LOCATOR DISPLAY
// GPS_DATE_TIME_MH
// V1.0 22-8-17 after fix, set RTC, then display RTC Time & Date
// jack
// TIP +5V
// ring 13 (RX LOW, FROMGPS)
// ring 12 (TX LOW, TOGPS)
// GND
// HEADERS & LIBRARIES
#include "Oled_128X64_I2C.h"
#include "SoftwareSerial.h"
// CONNECTIONS
#define FROMGPS 13
#define TOGPS 12
#define SW 4
// I2C COMMS DATA
// RTC address
#define RTCADDR 0x68
// OBJECTS
SoftwareSerial gps(FROMGPS, TOGPS);
// GLOBAL VARIABLES
char gpsbuf[200]; // GPS input buffer
char tm[20]; // time HHMMSS
char fix[5]; // fix A|V, init void
char dt[20]; // date YYMMDD
char la[15]; // latitude
char ns[2]; // NS
char lo[15]; // longitude
char ew[2]; // EW
// data converted to numeric (decimal) bytes
byte hrs, mns, sec;
byte yr, mth, dy;
byte dow;
double lat, lon;
// Maidenhead Locator
char mh[10] = "";
// SETUP
void setup() {
// pin modes
pinMode(FROMGPS, INPUT);
pinMode(TOGPS, OUTPUT);
pinMode(SW, INPUT_PULLUP);
// OLED init, I2C addr 0x3C
oled.begin();
// GPS serial init
gps.begin(9600);
strcpy(fix, "V"); // init no fix
}
// LOOP
void loop() {
dispUpdate(); // init display AWTG GPS
do { // read sentence until fix confirmed
getGPS("$GPRMC", gpsbuf); // get $GPRMC
fieldsGPS(gpsbuf); // dig out ASCII fields
} while (strcmp(fix, "A") != 0);
convert(); // convert Time & Date to numeric values
dow = calcDow(yr, mth, dy); // calulate day of week
setRTC(); // program RTC with GPS time
lat = convertPos(la, ns); // convert Lat & Lon to numeric
lon = convertPos(lo, ew);
findMH(mh, lat, lon); // find Maidenhead Locator
while (!button()) { // update Time & Date display from RTC
readRTC(); // read RTC
dispUpdate();
}
strcpy(fix, "V"); // button pressed restart
}
// button pressed?
bool button() {
if (digitalRead(SW) == LOW) { // button pressed?
while (!digitalRead(SW)); // wait for release
return true;
}
else {
return false;
}
}
// GPS FUNCTIONS
// get sentence into buffer, for example
// $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
void getGPS(char *sntc, char *buf) {
do { // find sntc
getline(buf);
} while (strncmp(buf, sntc, 6) != 0);
}
// get a line from the GPS, inc /r/n, add /0
void getline(char *line) {
char c;
int p;
p = 0; // buffer pointer
do {
if (gps.available() > 0) { // data?
c = gps.read(); // read character
line[p++] = c; // put in buffer
}
} while ( c != '\n' ); // stop on /n
line[p] = '\0'; // terminate string
}
// find GPS fields
void fieldsGPS(char *buf) {
// extract strings from $GPRMC fields
xtract(buf, 1, tm); // time HHMMSS
xtract(buf, 2, fix); // fix A or V
xtract(buf, 9, dt); // date YYMMDD
xtract(buf, 3, la); // latitude
xtract(buf, 4, ns); // NS
xtract(buf, 5, lo); // longitude
xtract(buf, 6, ew); // EW
}
// extract field and return string in outbuf
void xtract(char *in, int field, char *out) {
int ip = 0; // input buffer pointer
int op = 0; // output buffer pointer
int f = 0; // field counter
while (f < field) { // find start of field, ip
while (in[ip++] != ',');
f++;
}
while (in[ip] != ',') { // scan to next ','
out[op++] = in[ip++]; // copy in to out
}
out[op] = '\0'; // terminate out string
}
// convert GPS ASCII Time and Date to decimal bytes
void convert() {
hrs = strtob(tm, 0); // HH....
mns = strtob(tm, 2); // ..MM..
sec = strtob(tm, 4); // ....SS
dy = strtob(dt, 0); // DD....
mth = strtob(dt, 2); // ..MM..
yr = strtob(dt, 4); // ....YY
}
// convert ASCII field starting at field pointer, to byte
byte strtob(char *field, int fp) {
char out[20];
strncpy(out, field + fp, 2); // copy 2 char
return (byte)atoi(out); // return byte
}
// convert Lat, Lon strings to decimal +/-NS|EW
double convertPos(char *pos, char *d) {
double pp, mm, ans;
int dd;
pp = atof(pos); // get in decimal ddmm.mmmmmmm
dd = (int)pp / 100; // get degrees part
mm = pp - (100 * dd); // get minutes
ans = dd + (double)mm / 60.0; // calc decimal degrees
if (strcmp(d, "N") == 0 || strcmp(d, "E") == 0) // if positive
return ans;
else
return - ans; // negative
}
// DATE OF WEEK CALC
// calc dow, return decimal byte, Sun = 0
byte calcDow(byte year, byte month, byte day)
{
unsigned long days;
unsigned int febs;
unsigned int months[] =
{
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 // days until 1st of month
};
days = year * 365; // days up to year
febs = year;
if (month > 2) febs++; // number of completed Februaries
// add in the leap days
days += ((febs + 3) / 4);
days -= ((febs + 99) / 100);
days += ((febs + 399) / 400);
days += months[month - 1] + day;
return (byte)(((days + 5) % 7)); // sun = 0
}
// FIND MAIDENHEAD LOCATOR
void findMH(char *dst, double fa, double fo) {
int a1, a2, a3;
int o1, o2, o3;
double rd;
// Latitude
rd = fa + 90.0;
a1 = (int)(rd / 10.0);
rd = rd - (double)a1 * 10.0;
a2 = (int)(rd);
rd = rd - (double)a2;
a3 = (int)(24.0 * rd);
// Longitude
rd = fo + 180.0;
o1 = (int)(rd / 20.0);
rd = rd - (double)o1 * 20.0;
o2 = (int)(rd / 2.0);
rd = rd - 2.0 * (double)o2;
o3 = (int)(12.0 * rd);
dst[0] = (char)o1 + 'A';
dst[1] = (char)a1 + 'A';
dst[2] = (char)o2 + '0';
dst[3] = (char)a2 + '0';
dst[4] = (char)o3 + 'A';
dst[5] = (char)a3 + 'A';
dst[6] = '\0';
}
// SET RTC
// set date and time bytes to RTC BCD
void setRTC() {
// program RTC
Wire.beginTransmission(RTCADDR);
Wire.write(0); // next input at sec register
Wire.write(decToBcd(sec)); // set seconds
Wire.write(decToBcd(mns)); // set minutes
Wire.write(decToBcd(hrs)); // set hours
Wire.write(decToBcd(dow)); // set day of week
Wire.write(decToBcd(dy)); // set date (1 to 31)
Wire.write(decToBcd(mth)); // set month (1-12)
Wire.write(decToBcd(yr)); // set year (0 to 99)
Wire.endTransmission();
}
// Convert decimal to BCD
byte decToBcd(byte dec)
{
return ( (dec / 10 * 16) + (dec % 10) );
}
// READ RTC
void readRTC() {
// Reset the RTC register pointer
Wire.beginTransmission(RTCADDR);
Wire.write(0x00);
Wire.endTransmission();
// request 7 bytes from the RTC address
Wire.requestFrom(RTCADDR, 7);
// get the time date
sec = bcdToDec(Wire.read()); // 0 - 59
mns = bcdToDec(Wire.read()); // 0 - 59
hrs = bcdToDec(Wire.read() & 0b111111); // mask 12/24 bit
dow = bcdToDec(Wire.read()); // 0 = Sunday
dy = bcdToDec(Wire.read()); // 1 - 31
mth = bcdToDec(Wire.read()); // 0 = jan
yr = bcdToDec(Wire.read()); // ..yy
}
// Convert BCD to decimal numbers
byte bcdToDec(byte val) {
return ( (val / 16 * 10) + (val % 16) );
}
// PICTURE LOOP
// this version displays MH
// this version displays lat/lon
void dispUpdate() {
oled.firstPage();
do {
dispMsg(40, 0, "GPS MH");
if (strcmp(fix, "A") != 0) { // no fix?
dispMsgL(30, 15, "AWTG GPS");
}
else {
dispMsgL(35, 15, mh);
dispDate(15, 32, dow, dy, mth, yr);
dispTimeL(25, 47, hrs, mns, sec);
}
} while ( oled.nextPage() );
}
No comments:
Post a Comment