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