In the last post I described how I wanted to build a GPS input to my VFO, and use this to display my Locator, from the Lat & Lon data, and calibrate the time and date of my RTC. There is a little progress I can report. the VFO has a new 3.5mm socket on the back to connect to the GPS. After a mixup, I have solved the problems of getting the right connections to the GPS and to the Arduino. Arduino pin 12 is the data input from the GPS, connected to the GPS TX output, and pin 13 is connected to the GPS RX input.
VFO with 3.5mm socket for GPS input
I have some code working - see below.
VFO with time and date set from GPS
Power up the VFO by USB. Then plug in the GPS. push the button and there is a GPS FIX the RTC will be programmed. The display will show the RTC date time and the Maidenhead Locator. WSPR or JT65 sketches can now be loaded on the VFO and will transmit at the correct time (on UTC minute)
It may be possible to integrate this GPS calibration into the WSPR and JT65 sketches, but there may not be enough memory to do this - I have a vague plan to store the MH in EEPROM so it accessible by WSPR programs.
CODE
// DATE_TIME_MH_SET_GPS // V2.5 read GPS time, fix & date, calc dow, set RTC, store MH // display RTC date & time, MH // GPS VK-163 jack connections // 1 tip VCC // 2 TX GPS output // 3 RX GPS input // 4 ring GND #include "Oled_128X64_I2C.h" #include "SoftwareSerial.h" #include "Wire.h" #include "EEPROM.h" // Arduino pins TX GPS -> RX(12), RX GPS <- TX(13), button #define RX 12 #define TX 13 #define SW 4 // RTC address #define RTCADDR 0x68 // GPS data buffer char gpsbuf[200]; // data extracted from $GPRMC, ACSII 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 // Maidenhead Locator char mh[10]; // RTC data, decimal byte hrs, mns, sec; byte yr, mth, dy; byte dow; double lat, lon; // Serial object SoftwareSerial gps(RX, TX); // ============= setup ============== void setup() { // pins pinMode(RX, INPUT); pinMode(TX, OUTPUT); pinMode(SW, INPUT_PULLUP); // I2C init Wire.begin(); // OLED init, I2C addr 0x3C oled.begin(); // GPS serial init gps.begin(9600); // init GPS void strcpy(fix, "V"); } // ============== loop =============== void loop() { dispUpdate(); // if buuton pressed, get GPS data if (digitalRead(SW) == LOW) { strcpy(fix, "V"); // set V for display update dispUpdate(); getGPS(); // get GPS, extract data if (strcmp(fix, "A") == 0) { // when GPS Aquired setRTC(); // set RTC date time dow setMH(); // calculate MH Locator } } getRTC(); // read RTC } // ========= GPS funcitons ========= // get RMC line data void getGPS() { do { getline(gpsbuf); } while (strncmp(gpsbuf, "$GPRMC", 6) != 0); // extract strings from $GPRMC fields xtract(gpsbuf, 1, tm); // time HHMMSS xtract(gpsbuf, 2, fix); // fix A or V xtract(gpsbuf, 9, dt); // date YYMMDD xtract(gpsbuf, 3, la); // latitude xtract(gpsbuf, 4, ns); // NS xtract(gpsbuf, 5, lo); // longitude xtract(gpsbuf, 6, ew); // EW } // get a line from the GPS, inc /r/n, add /0 void getline(char *out) { char c; int p; p = 0; // buffer pointer do { if (gps.available() > 0) { // data? c = gps.read(); // read character out[p++] = c; // put in buffer } } while ( c != '\n' ); // stop on /n out[p] = '\0'; // terminate string } // 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 } // ================ RTC functions SET ================= // set date and time bytes to RTC BCD void setRTC() { // get GPS data in bytes, calc dow 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 dow = calcDow(yr, mth, dy); // 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 ASCII (0-99), starting at bp, to byte byte strtob(char *in, int bp) { char out[20]; strncpy(out, in + bp, 2); // copy 2 char return (byte)atoi(out); // return byte } // Convert decimal to BCD byte decToBcd(byte dec) { return ( (dec / 10 * 16) + (dec % 10) ); } // calc dow 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; // now we have day number such that 0000-01-01(Sat) is day 1 return (byte)(((days + 5) % 7)); } // ====================Maidenhead functions================ // calculate maideng=head locator from lat & lon void setMH() { // extract lat * lon from GPS data xtract(gpsbuf, 3, la); xtract(gpsbuf, 4, ns); xtract(gpsbuf, 5, lo); xtract(gpsbuf, 6, ew); lat = convertPos(la, ns); lon = convertPos(lo, ew); findMH(mh, lat, lon); saveMH(); // save in EEPROM } // 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 } // find MH from lat & lon 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'; } // save mh in EEPROM bytes 0-6 void saveMH() { int addr; byte i; addr = 0; i = 0; while(i < strlen(mh)) // write MMnnMM to EEPROM EEPROM.write(addr++, mh[i++]); EEPROM.write(addr, '\0'); // terminate string } // ====================RTC functions READ ================= // get time from RTC, convert BCD to decimal void getRTC() { // Reset the RTC register pointer Wire.beginTransmission(RTCADDR); Wire.write(0); 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 Display =============== // picture loop void dispUpdate() { oled.firstPage(); do { dispMsg(55, 0, "GPS"); if (strcmp(fix, "V") == 0) { // no fix V dispMsgL(30, 15, "GET GPS"); dispMsg(25, 45, "Press Button"); } else { dispMsgL(30, 15, mh); // fix A dispDate(15, 32, dow, dy, mth, yr); dispTimeL(25, 45, hrs, mns, sec); } } while ( oled.nextPage() ); }