Saturday 24 June 2017

BASIC Tech Group - MyNews - 47 GPS time and locator

My ADS (analog Digital Synthesiser) built using an Arduino UNO and an AD9851 chip includes a RTC DS3231 with a back up battery. The intention is to have UTC time available to software running on the Arduino UNO. So that it can generate correctly timed WSPR and JT65 output. It also includes a MMIC amplifier to output up to 10mW into 50R.

IMG 1084

Internal view of the VFO

TIME

At the moment I have a special Arduino sketch "DATE_TIME_SET_OLED" - see below - which I use to set the date and time in the RTC, it needs you to enter the date & time in the format YYMMDDWHHMMSS (W = day of week, Sunday = 1) in the Arduino Monitor window, then hit "Send" at exactly the right moment to set the correct time. Obviously this is a bit hit-and-miss, and also relies on your Computer displaying the right time to the second (my MacBook does this automatically by reading time from time.apple.com, but I have had trouble with my Windows PC which loses lock).

Anyway it seemed to me that by providing a new input/output connection to the Arduino UNO in the VFO I could send in information from a GPS receiver, extract the date and time and automatically calibrate the internal RTC. I could then also use the same connection in other sketches to input or output a couple of signals to any external device when the GPS is not used...

GPS RECEIVER

First a GPS receiver, I searched the internet and found a very low cost solution - GPS receivers that are targeted at car navigation and dash board cameras market. Like this one, VK-163 G-Mouse Headphone Wire Interface Navigation GPS,

Screen Shot 2017 06 24 at 14 50 36

GPS Receiver

It has a 4 way 3.5mm jack plug. After a considerable time fussing about with it I discovered the connections, which are,

Jack tip = VCC 3.6-5V

1st ring = GPS TX output (NMEA output data strings)

2nd ring = GPS RX Input (configuration input commands)

Jack shaft = Ground

And I wired it up to an Arduino UNO, using pin 12 as UNO RX to GPSTX, and pin 13 as UNO TX to GPS RX. />
IMG 1220

GPS wiring to Arduino UNO

The Arduino connections I used can be read in the sketch below. Basically pin 12 for data coming in, and pin 13 for any commands I may want to send out. Though I found the GPS works out of the box with 1 second updated outputs without giving any new commands, so I haven't used pin 13 in my sketch.

The results, when I insert Serial.print() commands, are that I can read the NMEA message "$GPRMC" on the serial monitor like this,

Screen Shot 2017 06 24 at 14 34 11

GPS NMEA ASCII string data for the $GPRMC message ID

Below are the sketches code for the manual set time and the GPS reception. Now all I have to do is extract the time and date info from the GPS string and program the RTC...

CODE

// DATE_TIME_SET_OLED
// V1.0 9-5-17 does not use DS3231 library
// enter YYMMDDwHHMMSS, reset/reload to repeat
// w = day-of-week 1 = mon, 01 = Jan 17 = 2017, 24 hour clock
// RTC
// SDA A4
// SCL A5
// SW 4

#include "Wire.h"
#include "Oled_128X64_I2C.h"

// RTC address
#define RTCADDR 0x68

// RTC time and date
byte doW, date, month, year;
byte hrs, mns, sec;

bool gotString;

void setup() {
  Serial.begin(9600);
  
  Wire.begin();

  oled.begin();

  gotString = false;
  
  dispUpdate();
}

void loop() {
  char inString[20] = "";
  byte j = 0;

  while (!gotString) {
    if (Serial.available()) {
      inString[j] = Serial.read();

      if (inString[j] == '\n') {
        gotString = true;

        // convert ASCII codes to bytes
        year = ((byte)inString[0] - 48) * 10 + (byte)inString[1] - 48;
        month = ((byte)inString[2] - 48) * 10 + (byte)inString[3] - 48;
        date = ((byte)inString[4] - 48) * 10 + (byte)inString[5] - 48;
        doW = ((byte)inString[6] - 48);
        hrs = ((byte)inString[7] - 48) * 10 + (byte)inString[8] - 48;
        mns = ((byte)inString[9] - 48) * 10 + (byte)inString[10] - 48;
        sec = ((byte)inString[11] - 48) * 10 + (byte)inString[12] - 48;

        setRTC();
      }
      j += 1;
    }
  }

  getRTC(); // get time
  
  dispUpdate();
}
  
// set the time int he RTC
void setRTC()
{
  // sets time and date data to DS3231
  Wire.beginTransmission(RTCADDR);
  Wire.write(0); // set next input to start at the 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 (1=Sunday, 7=Saturday)
  Wire.write(decToBcd(date)); // set date (1 to 31)
  Wire.write(decToBcd(month)); // set month
  Wire.write(decToBcd(year)); // set year (0 to 99)
  Wire.endTransmission();
}

// get time from RTC, convert bcd to decimal
void getRTC() {
  // Reset the RTC register pointer
  Wire.beginTransmission(RTCADDR);
  Wire.write(0x00); // output start at sec register
  Wire.endTransmission();

  // request 7 bytes from the RTC address
  Wire.requestFrom(RTCADDR, 7);

  // get the time data
  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 - 6 = Sunday - Saturday
  date = bcdToDec(Wire.read()); // 1 - 31
  month = bcdToDec(Wire.read()); // 0 = jan
  year = bcdToDec(Wire.read()); // 20xx
}

// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return( (val/16*10) + (val%16) );
}

// picture loop, display init data
void dispUpdate() {
  oled.firstPage();
  do {
    if (gotString == true) {
      dispDate(15, 15, doW, date, month, year);
      dispTimeL(25, 40, hrs, mns, sec);
    }
    else {
      dispMsg(0, 15, ">> YYMMDDwHHMMSS");
    }
  } while ( oled.nextPage() );
}


MORE CODE
// GPS_READ_MSG_PRINT
// V0.4 basics of read GPS
// Jack plug/socket wiring
// tip  VCC (Y)
// 2    TX GPS (R)
// 3    RX GPS (OR)
// ring GND (BWN)

#include "SoftwareSerial.h"

// connections GPSRX -> RX, GPSTX <- TX
#define RX 12
#define TX 13

// GPS data buffer, gps message ID
char gpsbuf[200];
char MSGID[10] = "$GPRMC";

// jack GPS 3(TX) -> RX, GPS 2(RX) <- TX
SoftwareSerial gps (RX, TX);

void setup() {
  pinMode(RX, INPUT);
  pinMode(TX, OUTPUT);

  Serial.begin(9600);

  gps.begin(9600); // start GPS serial

  Serial.println("Start");
}

void loop() {
  // read MSGID line
  do {
    getline(gpsbuf);
  } while (strncmp(gpsbuf, MSGID, 6) != 0);
  
  Serial.print(gpsbuf);
}

// get a line from the GPS, inc /r/n, add /0
void getline(char *buf) {
  char c;
  int p = 0;

  do {
    if (gps.available() > 0) {
      c = gps.read();
      buf[p++] = c;
    }
  } while ( c != '\n');
  buf[p] = '\0';
}

1 comment:

manu said...

pls edit the code this out in absence of millisecond format