Uno/Ultimate GPS Timing problem

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
speedobob
 
Posts: 19
Joined: Sun Dec 28, 2014 7:11 pm

Uno/Ultimate GPS Timing problem

Post by speedobob »

Am I expecting too much?
I have an Adafruit Ultimate GPS Logger shield driving a UNO R3 and I need a source of accurate, one second, timing to be able to calculate average times, distances etc.
I have two Seven segment displays and a 16x2 LCD module all driven by I2C.
I wanted to use the transition of the GPS seconds output to create a basic one second timer but discovered that the GPS time output 'misses a beat' periodically (and randomly).
So I tried the GPS PPS output (which I expected to be unaffected by my software in that it shouldn't be associated with anything else in the GPS communications) and found that this too misses occasional seconds and is not reliable. So I created a 'pseudo beat'. If a beat doesn't arrive from the GPS PPS for 1.1 seconds (timed by 'millis()') from the last one, I increment my counter anyway. This improved matters but I still get a two minute discrepancy every thirty minutes. Not good.
I tried creating a timer with the normal 'millis()' function but that just goes crazy as though the millis are just random.
So my conclusion has to be there is interference between the interrupts running the GPS and the I2C transmission system.
Can anyone please help?
Bob

User avatar
Franklin97355
 
Posts: 23938
Joined: Mon Apr 21, 2008 2:33 pm

Re: Uno/Ultimate GPS Timing problem

Post by Franklin97355 »

Perhaps, if you post your code and a description or drawing of your connections between it all we can help. Please use the code button "</>" or

Code: Select all

 tags when posting code to the forums.

User avatar
speedobob
 
Posts: 19
Joined: Sun Dec 28, 2014 7:11 pm

Re: Uno/Ultimate GPS Timing problem

Post by speedobob »

Code: Select all

#include <SoftwareSerial.h>
#include <Adafruit_GPS.h>  //GPS stuff derived from the 'parsing' example
SoftwareSerial mySerial(8, 7);
Adafruit_GPS GPS(&mySerial);

#include <LiquidCrystal.h>  //this is a special library from adafruit to take care if lcd transmission over I2C
//LiquidCrystal lcd(6, 12, 5, 4, 3, 11);  //Reg Select, Enable, DB4, DB5, DB6, DB7
LiquidCrystal lcd(0);
#include <Wire.h>
#define bluesegmentdis 0x72 //This is the default address of the blue OpenSegment display 
#define redsegmentdis 0x71 //This is the default address of the redOpenSegment display 

unsigned long segdel = 0;
//unsigned long lcddel = millis();
boolean dispon = false;

boolean allowmonitor = false;  //disables serial monitor. True enables it. Keep it on false because the serial.display takes up a lot of time
boolean rundiags = false;
byte testcount = 0;
int testcounttot = 0;

//int cycles = 0;  //for I2C test only

const byte displayinhibit = 0;  //was int

const byte sw1centrepin = 2;  //this is analog 2      //was int
const byte stopstartsw = 3;  //this is analog 3      //was int
const byte sw2centrepin = 1;  //this is analog 1      //was int

boolean bst = false;
byte bsthour = 0;

int sw1val, sw1valold, sw1mode;
int sw1variant = 205;  //it should actually be 204.6. Sw1 has 6 positions
int sw2val, sw2valold, sw2mode;
int sw2variant = 205;  //sw2 has 6 positions
byte fudge = 10;  //was 15 int
byte fudge2 = 20;//was int
byte bouncetime = 100;//was int

boolean displayinhibitval = false;


unsigned int oldseconds = 0;
//unsigned int numsecsfreeze = 0;

float boatspeed1 = 0.0000;

int maxspeed = 0;
int boatspeed = 0;
int heading = 0;

float totdist = 0.0000;
float tensecdist = 0.000000;
float avgspdcum = 0;
float avgspdavg = 0.0000;

int totdistdis = 0;

int avgspdgo = 0;

boolean avgspdflag = false;
int avgspdcount = 0;

int BANNED = 0;
int minss = 500;

byte dishour = 0;

int elapsecs, elapmins, elaphours;
boolean startseq = false;
unsigned long latesttime = 0;
unsigned long elaplen = 0;
unsigned long elapstart = 0;
unsigned long elaptest = 0;

int avgcount = 0;
float  avgspeed = 0.0000;
boolean avgspeedflag = false;

byte engonhour = 0;
byte engonmin = 0;
byte engoffhour = 0;
byte engoffmin = 0;
boolean engtiming = false;
int onmins = 0;
int offmins = 0;
int ontime = 0;

const byte ppsin = 2;  //pulse per second from the satnav
int ppsinval = LOW;
int numsecs = 0;
int numsecsold = 0;
int nummins = 0;
int numhours = 0;

unsigned long sectimer = 0;
unsigned long distimer = millis();
boolean ppson = false;
boolean syncon = false;
const byte ledgreen = 9;
const byte ledyell = 10;
boolean ledon;
int pseudocount = 0;
int pseudocounttot = 0;



int stopstartswval = 0;
int oldstopstartswval = 0;
boolean startjourney = false;
boolean endjourney = false;
boolean journeyrunning = false;

byte segbright = 250;  //was 10
boolean flash = false;
#define GPSECHO  false // Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console. Set to 'true' if you want to debug and listen to the raw GPS sentences. 

boolean usingInterrupt = false;// this keeps track of whether we're using the interrupt.

void setup() {
  Wire.begin(); //Join the bus as master
  //Send the reset command to the display - this forces the cursor to return to the beginning of the display
  Wire.beginTransmission(bluesegmentdis);
  Wire.write('v');
  Wire.write(0x7A);
  Wire.write(segbright);
  Wire.endTransmission();

  Wire.beginTransmission(redsegmentdis);
  Wire.write('v');
  Wire.write(0x7A);
  Wire.write(segbright);
  Wire.endTransmission();

  lcd.begin(16, 2);
  lcd.clear();  //clears lcd on startup
  pinMode(displayinhibit, INPUT);
  digitalWrite(displayinhibit, HIGH);  //initiates the PULL UP resistor

  pinMode(ledgreen, OUTPUT);
  pinMode(ledyell, OUTPUT);
  pinMode(ppsin, INPUT);
  digitalWrite(ppsin, LOW);//initiates the PULL UP resistor


  Serial.begin(115200);  //115200);  // connect at 115200 so we can read the GPS fast enough and echo without dropping chars. MAKE SURE MONITOR ALSO SET TO 115200
  /*
  if (allowmonitor) {
    Serial.println("GPS test!");
  }
  else {
    Serial.println("You disabled the monitor!  Set 'allowmonitor' to 'true' in configuration.");
  }
  */
  GPS.begin(9600);
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);   // 1 Hz update rate. Only accepts 1, 5 or 10
  useInterrupt(true);
  delay(1000);
  mySerial.println(PMTK_Q_RELEASE);  // Ask for firmware version
}
// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
  char c = GPS.read();
  // if you want to debug, this is a good time to do it!

#ifdef UDR0
  if (GPSECHO)
    if (c) UDR0 = c;
  // writing direct to UDR0 is much much faster than Serial.print
  // but only one character can be written at a time.
#endif

}  //end of void

void useInterrupt(boolean v) {
  if (v) {
    // Timer0 is already used for millis() - we'll just interrupt somewhere
    // in the middle and call the "Compare A" function above
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
  }
  else {
    // do not call the interrupt function COMPA anymore
    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
  }
}    //end of void
uint32_t timer = millis();

void loop() {

  if (rundiags) {
    testcounttot ++;
    if (testcounttot == 10001) {
      testcounttot = 0;
    }
    diagnostics(testcounttot);
  }
  else {
    if (GPS.newNMEAreceived()) {  //read the gpslines obtained by the interrupt. If a sentence is received, check the checksum, parse it...
      if (!GPS.parse(GPS.lastNMEA()))   // this also sets the newNMEAreceived() flag to false
        return;  // we can fail to parse a sentence in which case we should just wait for another
    }
    if (timer > millis())  timer = millis();  // if millis() or timer wraps around, we'll just reset it

    //start my programs
    setdismode();  //manipulates the lcd modes
    ticktimer();  //produces a 'seconds' counter controlled by engine on/off
    setbst();  //calculate british summer time
    massagegpsout();  //manipulate gps figures. Convert knots to mph
    calcjourney();  //journey timing stuff

    disponoff();//sense engine on/off and control the cockpit display

    if (millis() > (segdel + 500)) { //activate 7 seg displays when the engine runs or the button is pressed.
      if (dispon == false) {
        lcd.setBacklight(LOW);
        Wire.beginTransmission(bluesegmentdis);
        Wire.write('v');
        Wire.endTransmission();
        Wire.beginTransmission(redsegmentdis);
        Wire.write('v');
        Wire.endTransmission();
      }
      else {
        lcd.setBacklight(HIGH);
        setcockmode();  //set the blue 7 seg display
        segmentred(boatspeed); //set the red 7 seg display
      }
      segdel = millis();
    }

    //  if (millis() > lcddel + 0) {
    // setdismode();  //set local lcd display
    //    lcddel = millis();
    //  }

    timeeng();  //start the engine on timer

    if (millis() - timer > 2000) {
      if (allowmonitor) {
        printgps();  //Calls the serial monitor. Timer originates in the gps interrupt so be careful. You have to un-comment all the print statements to keep the variables dynamic memory down
      }
      timer = millis();
    }

  }  //end of diagnostic test

}  //end of void

void setdismode() { // Timing comes from loop
  readsw1();
  switch (sw1mode) {
    case 0:
      display3();
      break;
    case 1:
      display4();  //Current Journey
      break;
    case 2:
      display1(); //Position lat and long
      break;
    case 3:
      display2();  //Date and time group
      break;
    case 4:
      display5();
      break;
    case 5:
      display6();
      break;
  }//end of switch

}  //end of void

void readsw1() { 
  sw1val = analogRead(sw1centrepin);
  if (sw1val < (sw1valold - fudge) || sw1val > (sw1valold + fudge)) {
    delay(bouncetime);
    lcd.clear();  //lcd is cleared on startup then each time the switch position changes
    sw1valold = sw1val;

  }
  //fudge is 15. sw1variant is 205
  if (sw1val <= (fudge)) { //<20 actually 0
    sw1mode = 0;
  }
  if (sw1val > (sw1variant - fudge) && sw1val < (sw1variant + fudge)) { //195 to 215 actually 202
    sw1mode = 1;
  }
  if (sw1val > ((sw1variant * 2) - fudge) && sw1val < ((sw1variant * 2) + fudge)) { //400 to 420 actually 407
    sw1mode = 2;
  }
  if (sw1val > ((sw1variant * 3) - fudge) && sw1val < ((sw1variant * 3) + fudge)) { //605 to 625 actually 613
    sw1mode = 3;
  }
  if (sw1val > ((sw1variant * 4) - fudge) && sw1val < ((sw1variant * 4) + fudge)) { //810 to 830 actually 818
    sw1mode = 4;
  }
  if (sw1val > ((sw1variant * 5) - fudge)) { //>1015 actually 1023
    sw1mode = 5;
  }
}  //end of void

void printgps() { //called from loop. Approximately every 2 seconds or so, print out the current stats
  if (allowmonitor) {
    printprint(0);
  }
}  //end of void

void massagegpsout() {
  //convert knots to mph then store the max
  boatspeed1 = GPS.speed * 1.15077944802;  //boatspeed1 is a float. convert knots to mph
  boatspeed = boatspeed1 * 100;
  //boatspeed = 6000;  //1234 is the integer equivalent of 12.34. Boatspeed is now an integer of the real boat speed multiplied by 100. For test only poo
  if (boatspeed < 2) { //it dithers when stationary
    boatspeed = 0.0;
  }
  if (boatspeed > maxspeed) {
    maxspeed = boatspeed;
  }
  if (boatspeed == 0) {
    heading = 999.0;  //to indicate that it's not valid because gps can't work out the heading if the boat is not moving
  }
  else {
    heading = GPS.angle;
  }

}  //end of void

void calcjourney() {  //calculates total distance travelled and average speed
  initjourn();  // //monitors stop start switch and defines journey start, journey running and end
  if (startjourney == true) { //startjourney lasts only as long as the switch is depressed.
    totdist = 0.0000;
    tensecdist = 0.0000;
    avgspdcum = 0.0000;
    avgspdcount = 0;
    avgspdavg = 0.0000;  //this is the 10 second average speed. Used to calculate totdist

    startseq = false;
    maxspeed = 0;
    avgspeed = 0.0000;
    elapsecs = 0;
    elapmins = 0;
    elaphours = 0;
    avgspeedflag = false;
  }

  if (journeyrunning == true && (startjourney == false)) {  //initiated by 'startjourney' but stays on even though switch is not operated
    // calc the average speed over 10 seconds.
    if (avgspdflag == false) {  //set up a ten second counter
      avgspdgo = numsecs;  //this is an int
      avgspdcum = 0.0000;  //a float
      avgspdcount = 0;  //an int
      avgspdflag = true;
    }

    if (numsecs > (avgspdgo + 9)) {  //leading edge of 10 seconds
      //boatspeed has been changed to an integer by multiplying by 100 to preserve 2 decimal places
      //avgspdavg is average miles per second over a ten second period. Is 1.667 @60mph. All times 100
      avgspdavg = ((float)avgspdcum / (float)avgspdcount) / (float)3600; //make the average then convert to miles per second. at 60mph this should be 1.67
      //so we travel a distance of avgspdavg every ten  seconds
      //  tensecdist = float(avgspdavg) * 10;  //this is the 10 second average speed expressed as an integer. Used to calculate totdist
      // totdist = totdist + tensecdist;  //distance travelled since time began. Divide by 100 to display
      totdist = totdist + avgspdavg;  //distance travelled since time began. Averaged over ten seconds. Divide by 100 to display
      avgspdcum = 0;
      avgspdcount = 0;
      avgspdgo = numsecs;
      avgspdflag = false;
    } else {
      avgspdcum = avgspdcum + boatspeed;
      avgspdcount += 1;
    }
  }

  elaptime();

  if (allowmonitor == true) {
    printprint(1);
  }
}  //end of void

void elaptime() { //poo
  //totdist is the running total of ten second distances
  if (journeyrunning == true) { //timing stuff friezes when the stop switch is activated
    if (startseq == false) {    //calculate elap seconds
      //A full 23:59:59 would be 86399 seconds OR 2:30:28 is 9028 seconds
      elapstart = ((((unsigned long)bsthour * 60) + (unsigned long)GPS.minute) * 60) + GPS.seconds;  //start time in seconds
      startseq = true;
    }//
  } else {
    startseq = false;
  }
  if (startseq == true) {
    //calculate running seconds
    latesttime =  elapstart + numsecs;
    elaplen = (float)latesttime - (float)elapstart;  //journey time in seconds.
    if ((numsecs % 10) == 0) { //average speed is only valid at the end of each ten second averaging period
      avgspeed = ((float)totdist * 10 / (float)100) / ((float)elaplen / (float)3600); //avg speed = (miles/100)/(time in secs/3600)
    }
    elaphours = (unsigned long) elaplen / (unsigned long) 3600;
    elapmins = ((unsigned long) elaplen - ((unsigned long) elaphours * (unsigned long) 3600)) / (unsigned long) 60;
    elapsecs = (unsigned long) elaplen - (((unsigned long) elaphours * (unsigned long) 3600) + ((unsigned long) elapmins * (unsigned long) 60));
  }
}  //end of void

void initjourn() {
  stopstartswval = analogRead(stopstartsw);  //a momentary, centre off toggle switch
  if (stopstartswval > (oldstopstartswval + fudge2) || stopstartswval < (oldstopstartswval - fudge2)) { //value varies between 509 and 500 with capacitors
    delay(bouncetime + 10);
    if (stopstartswval < 400) {
      startjourney = true;
    }
    else {
      startjourney = false;
    }
    if (stopstartswval >= 600) {
      endjourney = true;
    }
    else {
      endjourney = false;
    }
    oldstopstartswval = stopstartswval;
  }
  if ((stopstartswval > 400) && (stopstartswval < 600)) {
    endjourney = false;
    startjourney = false;
  }

  if (startjourney == true) {
    journeyrunning = true;
  }
  if (endjourney == true) {
    journeyrunning = false;
  }
}  //end of void

void display1() { //position lat and long
  if (GPS.fix) {
    lcd.setCursor(0, 0);
    lcd.print("Lat ");
    lcd.print(GPS.latitude, 4);
    lcd.print(" ");
    lcd.print(GPS.lat);
    lcd.setCursor(0, 1);
    lcd.print("Lon ");
    lcd.print("0");
    lcd.print(GPS.longitude, 4);
    lcd.print(" ");
    lcd.print(GPS.lon);
  }
  else {
    lcd.setCursor(0, 0);
    lcd.print("No Fix ");
  }
}  //end of void

void display2() {  //date and time
  lcd.setCursor(0, 0);
  lcd.print("DT");
  if (GPS.day < 10) {
    lcd.print("0");
  }
  lcd.print(GPS.day);
  lcd.print("/");
  if (GPS.month < 10) {
    lcd.print("0");
  }
  lcd.print(GPS.month);
  lcd.print(" ");
  dishour = bsthour ;
  if (dishour < 10) {
    lcd.print("0");
  }
  lcd.print(dishour);
  lcd.print(":");
  if (GPS.minute < 10) {
    lcd.print("0");
  }
  lcd.print(GPS.minute);
  lcd.print(":");
  if (GPS.seconds < 10) {
    lcd.print("0");
  }
  lcd.print(GPS.seconds);
}  //end of void

void display3() { //current speed
  if (GPS.fix) {
    lcd.setCursor(0, 0);
    // if (boatspeed < 10) {
    //   lcd.print("0");
    //}
    float boatspeed2 = boatspeed;
    boatspeed2 = boatspeed2 / 100;
    lcd.print(boatspeed2, 1);
    lcd.print("Mph ");
    lcd.print("Max ");
    // if (maxspeed < 10) {
    //   lcd.print("0");
    // }
    float maxspeed1 = maxspeed;
    maxspeed1 = maxspeed1 / 100;
    lcd.print(maxspeed1, 1);
    lcd.setCursor(0, 1);
    lcd.print("Sats ");
    if (GPS.satellites < 10) {
      lcd.print("0");
    }
    lcd.print(GPS.satellites);
    lcd.setCursor(8, 1);
    lcd.print("Dir ");
    // heading =0;
    /*
    if (heading < 10) {
      lcd.print("000");
    }
    if (heading < 100) {
      lcd.print("0");
    }
    */
    lcd.print(heading);
    //   lcd.write(B11011111); //little square
  }
  else {
    lcd.setCursor(0, 0);
    lcd.print("No Fix ");
  }
}  //end of void

void display4() { //current journey
  lcd.setCursor(0, 0);
  lcd.print("Journey ");
  if (elaphours < 10) {
    lcd.print("0");
  }
  lcd.print(elaphours);
  lcd.print(":");
  if (elapmins < 10) {
    lcd.print("0");
  }
  lcd.print(elapmins);
  lcd.print(":");
  if (elapsecs < 10) {
    lcd.print("0");
  }
  lcd.print(elapsecs);
  lcd.print(" sec ");
  lcd.print(numsecs);

  lcd.setCursor(0, 1);
  lcd.write(B01111110); //right arrow
  if (totdist < 10.0000) {
    lcd.print("0");
  }
  lcd.print((totdist / 10), 2);
  lcd.print("M");

  lcd.setCursor(8, 1);
  lcd.print("avg ");

  if (avgspeed > 9999) {
    lcd.print("XXXX");
  } else {
    lcd.print((avgspeed));
  }

}  //end of void

void display5() {  //poo
  /*
    lcd.setCursor(0, 0);
    lcd.print("numsecs ");
    lcd.print(numsecs);
    lcd.setCursor(0, 1);

    lcd.print(" t ");
    lcd.print(totdist / 100);
    */
  /*
  lcd.setCursor(0, 0);
  lcd.print("start ");
  lcd.print(elapstart);
  lcd.setCursor(0, 1);
  lcd.print("L ");
  lcd.print(latesttime);
  lcd.print(" T ");
  lcd.print(elaplen);
  */
  lcd.setCursor(0, 0);
  lcd.print("e ");
  lcd.print(elaplen);
  lcd.print(" d ");
  lcd.print(totdist * 10, 4);
  lcd.setCursor(0, 1);
  lcd.print("avgspdavg ");
  lcd.print(avgspdavg, 4);


  /*
    lcd.setCursor(0, 0);
    lcd.print(" qual ");
    lcd.print((int)GPS.fixquality);
    lcd.setCursor(0, 1);
    lcd.print("num");
    lcd.print(numsecs);
    lcd.print(" pps");
    lcd.print(ppsinval);
    lcd.print(" fix ");
    lcd.print(GPS.fix);

   lcd.setCursor(0, 0);
      lcd.print(" totdis ");
      lcd.print((totdist / 100), 2);
      lcd.setCursor(0, 1);
      lcd.print("s ");
      lcd.print(avgspeed);

      */

}  //end of void

void display6() { //engine run time
  lcd.setCursor(0, 0);
  lcd.print("On ");
  if (engonhour < 10) {
    lcd.print("0");
  }
  lcd.print(engonhour);
  lcd.print(":");
  if (engonmin < 10) {
    lcd.print("0");
  }
  lcd.print(engonmin);
  lcd.setCursor(9, 0);
  lcd.write(B01111110); //right arrow
  lcd.print(" ");
  if (engoffhour < 10) {
    lcd.print("0");
  }
  lcd.print(engoffhour);
  lcd.print(":");
  if (engoffmin < 10) {
    lcd.print("0");
  }
  lcd.print(engoffmin);
  lcd.setCursor(0, 1);
  lcd.print("Eng ON ");
  if ((ontime / 60) < 10) {
    lcd.print("0");
  }
  lcd.print(ontime / 60);
  lcd.print(":");
  if ((ontime % 60) < 10) {
    lcd.print("0");
  }
  lcd.print(ontime % 60);  //this is modulo 60. Just leaves the remainder

}  //end of void

void setcockmode() { //Do this every second or so. Timing comes from loop
  readsw2();
  switch (sw2mode) {
    case 0:
      cockdis0(heading);  //current heading
      break;
    case 1:  //distance
      totdistdis = totdist*10;
      cockdis3(totdistdis);
      break;
    case 2:
      cockdis1(elaphours, elapmins);  //elap time
      break;
    case 3:
      cockdis2(bsthour, GPS.minute); //real time
      break;
    case 4:
      //GPS.satellites = 1;
      cockdis4(GPS.satellites); //num sats
      break;
    case 5:
      float bigalt;
      //GPS.altitude = 5;
      bigalt = GPS.altitude * 3.2808;  //1 meter = 3.2808 feet. Times 10 to get 1 decimal place displayed
      cockdis5(bigalt); //altitude
      break;
  }//end of switch
}  //end of void

void readsw2() { //cockpit rotary
  sw2val = analogRead(sw2centrepin);

  //sw2val = 0;  //for test
  if (sw2val < (sw2valold - fudge2) || sw2val > (sw2valold + fudge2)) {
    delay(bouncetime + 10);
    sw2valold = sw2val;
  }
  if (sw2val <= (fudge2)) { //sw2variant is 205. Fudge2 is 20
    sw2mode = 0;
  }
  if (sw2val > (sw2variant - fudge2) && sw2val < (sw2variant + fudge2)) {
    sw2mode = 1;
  }
  if (sw2val > ((sw2variant * 2) - fudge2) && sw2val < ((sw2variant * 2) + fudge2)) {
    sw2mode = 2;
  }
  if (sw2val > ((sw2variant * 3) - fudge2) && sw2val < ((sw2variant * 3) + fudge2)) {
    sw2mode = 3;
  }
  if (sw2val > ((sw2variant * 4) - fudge2) && sw2val < ((sw2variant * 4) + fudge2)) {
    sw2mode = 4;
  }
  if (sw2val > ((sw2variant * 5) - fudge2)) {
    sw2mode = 5;
  }
}  //end of void

void cockdis0(int displaynumber) { //Display Heading
  //displaynumber = 180;
  Wire.beginTransmission(bluesegmentdis); // transmit to device #1
  Wire.write('v');
  if (displaynumber == 999.0) {
    Wire.write(" ");
    Wire.write("O");
    Wire.write("F");
    Wire.write("F");
  }
  else {
    Wire.write(" "); //blank the left most digit
    displaynumber %= 1000; //Now remove the left most digit from the number we want to display
    //Wire.write(displaynumber / 1000); //Send the left most digit
    //displaynumber %= 1000; //Now remove the left most digit from the number we want to display
    Wire.write(displaynumber / 100);
    displaynumber %= 100;
    Wire.write(displaynumber / 10);
    displaynumber %= 10;
    Wire.write(displaynumber); //Send the right most digit
  }
  Wire.endTransmission(); //Stop I2C transmission
}  //end of void

void cockdis1(int displaynumber, int displaynumber2) { //elap time
  flash = ! flash;
  Wire.beginTransmission(bluesegmentdis);
  Wire.write('v');
  displaynumber = displaynumber * 100;
  Wire.write(displaynumber / 1000); //Send the left most digit
  displaynumber %= 1000; //Now remove the left most digit from the number we want to display
  Wire.write(displaynumber / 100);
  displaynumber2 = displaynumber2 * 100;
  Wire.write(displaynumber2 / 1000); //Send the left most digit
  displaynumber2 %= 1000; //Now remove the left most digit from the number we want to display
  Wire.write(displaynumber2 / 100);
  if (flash == true) {
    Wire.write(0x77); //insert decimal point
    Wire.write(2); //at position 2
  }
  Wire.endTransmission(); //Stop I2C transmission
}  //end of void

void cockdis2(int displaynumber, int displaynumber2) { //real time
  //  flash = ! flash;
  Wire.beginTransmission(bluesegmentdis);
  Wire.write('v');
  displaynumber = displaynumber * 100;
  Wire.write(displaynumber / 1000); //Send the left most digit
  displaynumber %= 1000; //Now remove the left most digit from the number we want to display
  Wire.write(displaynumber / 100);
  displaynumber2 = displaynumber2 * 100;
  Wire.write(displaynumber2 / 1000); //Send the left most digit
  displaynumber2 %= 1000; //Now remove the left most digit from the number we want to display
  Wire.write(displaynumber2 / 100);
  if (flash == true) {
    Wire.write(0x77); //insert decimal point
    Wire.write(2); //at position 2
  }
  Wire.endTransmission(); //Stop I2C transmission
}  //end of void

void cockdis3(int displaynumber) { // totdist
 
  Wire.beginTransmission(bluesegmentdis); // transmit to device #1
  Wire.write('v');
  Wire.write(displaynumber / 1000); //Send the left most digit
  displaynumber %= 1000; //Now remove the left most digit from the number we want to display
  Wire.write(displaynumber / 100);
  displaynumber %= 100;
  Wire.write(displaynumber / 10);
  displaynumber %= 10;
  Wire.write(displaynumber); //Send the right most digit
  Wire.write(0x77); //insert decimal point
  Wire.write(2); //at position 2
  Wire.endTransmission(); //Stop I2C transmission
}  //end of void

void cockdis4(int displaynumber) { //satellites
  Wire.beginTransmission(bluesegmentdis); // transmit to device #1
  Wire.write('v');  //clear display
  if (displaynumber < 1000) {
    Wire.write("-");  //clear digit
  }
  else {
    Wire.write(displaynumber / 1000); //Send the left most digit
    displaynumber %= 1000; //Now remove the left most digit from the number we want to display
  }
  if (displaynumber < 100) {
    Wire.write("-");  //clear digit
  }
  else {
    Wire.write(displaynumber / 100);
    displaynumber %= 100;
  }
  if (displaynumber < 10) {
    Wire.write("-");
  }
  else {
    Wire.write(displaynumber / 10);
    displaynumber %= 10;

  }
  Wire.write(displaynumber); //Send the right most digit
  Wire.endTransmission(); //Stop I2C transmission
}  //end of void

void cockdis5(int displaynumber) { //altitude
  Wire.beginTransmission(bluesegmentdis); // transmit to device #1
  Wire.write('v');
  if (displaynumber < 1000) {
    Wire.write("A");  //clear digit
  }
  else {
    Wire.write(displaynumber / 1000); //Send the left most digit
    displaynumber %= 1000; //Now remove the left most digit from the number we want to display
  }
  if (displaynumber < 100) {
    Wire.write(" ");  //clear digit
  }
  else {
    Wire.write(displaynumber / 100);
    displaynumber %= 100;
  }

  if (displaynumber < 10) {
    Wire.write(" ");  //clear digit
  }
  else {
    Wire.write(displaynumber / 10);
    displaynumber %= 10;
  }
  Wire.write(displaynumber); //Send the right most digit
  //  Wire.write(0x77); //insert decimal point
  //  Wire.write(4); //at position 4
  Wire.endTransmission(); //Stop I2C transmission
}  //end of void

void segmentred(int displaynumber) { //speed
  Wire.beginTransmission(redsegmentdis); // transmit to device #1
  if (displaynumber < 1000) {
    Wire.write(" ");  //clear digit
  }
  else {
    Wire.write(displaynumber / 1000); //Send the left most digit
    displaynumber %= 1000; //Now remove the left most digit from the number we want to display
  }

  Wire.write(displaynumber / 100);
  displaynumber %= 100;
  Wire.write(displaynumber / 10);
  displaynumber %= 10;
  Wire.write(displaynumber); //Send the right most digit
  Wire.write(0x77); //insert decimal point
  Wire.write(2); //at position 2
  Wire.endTransmission(); //Stop I2C transmission
} //end of void

void disponoff() { //sense engine on/off and control the cockpit display
  displayinhibitval = digitalRead(displayinhibit);
  delay(bouncetime);
  // displayinhibitval = false;
  if (displayinhibitval == true) {
    dispon = false;
    lcd.setBacklight(LOW);
  }
  else {
    dispon = true;
    lcd.setBacklight(HIGH);
  }
}

void diagnostics(int testtarget) {
  testcount ++;

  if (testcount == 10) {
    testcount = 0;
  }

  sw2val = analogRead(sw2centrepin);
  initjourn(); //monitors stop start switch and defines journey start, journey running and end
  sw1val = analogRead(sw1centrepin);

  Wire.beginTransmission(bluesegmentdis); // transmit to device #1
  Wire.write('v');
  Wire.write(testcount);
  Wire.write(testcount);
  Wire.write(testcount);
  Wire.write(testcount);
  Wire.endTransmission(); //Stop I2C transmission
  //testtarget = 1234;
  Wire.beginTransmission(redsegmentdis); // transmit to device #1
  Wire.write('v');
  Wire.write(testtarget / 1000); //Send the left most digit
  testtarget %= 1000; //Now remove the left most digit from the number we want to display
  Wire.write(testtarget / 100);
  testtarget %= 100;
  Wire.write(testtarget / 10);
  testtarget %= 10;
  Wire.write(testtarget); //Send the right most digit
  Wire.write(0x77); //insert decimal point
  Wire.write(2); //at position 2
  Wire.endTransmission(); //Stop I2C transmission

  /*
    Wire.beginTransmission(redsegmentdis); // transmit to device #1
   Wire.write('v');
   Wire.write(testcount);
   Wire.write(testcount);
   Wire.write(testcount);
   Wire.write(testcount);
   Wire.endTransmission(); //Stop I2C transmission
   */

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Test Up ");
  lcd.print(testcounttot);
  lcd.setCursor(0, 1);
  lcd.print("Test Down ");
  lcd.print(10000 - testcounttot);

  delay(500);

}  //end of loop

void setbst() {
  if ((GPS.month == 3) && (GPS.day > 24)) {
    bst = true;
  }
  else {
    bst = false;
  }
  if (GPS.month > 3) {
    bst = true;
  }

  if ((GPS.month == 10) && (GPS.day > 24)) {
    bst = false;
  }
  if (GPS.month > 10) {
    bst = false;
  }
  if (bst = true) {
    bsthour = GPS.hour + 1;
  } else {
    bsthour = GPS.hour;
  }
}  //end of void

void timeeng() {
  if (engtiming == false) {
    if (displayinhibitval == false) {  //engine started

      engonhour = bsthour;
      engonmin = GPS.minute;
      onmins = (engonhour * 60) + engonmin;
      engtiming = true;
    }
  }
  if (engtiming == true) {
    engoffhour = bsthour;
    engoffmin = GPS.minute;
    offmins = (engoffhour * 60) + engoffmin;
    ontime = offmins - onmins;
    if (displayinhibitval == true) {  //engine stopped
      engtiming = false;
    }
  }
}  //end of void

void ticktimer() {

  /* ITHINK
  Due to timing issues involved in the I2C and the serial data from/to the GPS, both the time from the GPS and the count (numsecs)
  from my counter both miss a beat occasionally. Doesn't matter for the GPS because the time will catch up on the next real time second.
  But it's serious for my counter because this counter (numsecs) is what's going to define distance and elapsed time etc.
  So I put in a timer. If I don't recieve a PPS within 1.1 seconds, I increment the counter anyway
  */
  if (GPS.fix) {
    if (startjourney == true) {
      numsecs = 0;
    }
    if (journeyrunning == true && (startjourney == false)) {
      ppsinval = digitalRead(ppsin);
      if (ppson == false) {
        if (ppsinval == HIGH) {
          pseudocount = 0;  //clear the pseudo counter each second
          numsecs += 1;
          sectimer = millis();  //start the missing pulse timer
          ppson = true;
        }
      }
      if (ppson == true && ppsinval == LOW) {
        ppson = false;
      }
      if ((millis() > (sectimer + 1100)) && ppsinval == LOW) { //means the pps has missed a beat
        numsecs += 1;  //so increment the counter to compensate
        pseudocount += 1;  //extra counts per second
        pseudocounttot += 1;  //total extra counts
        sectimer = millis();
      }
      //flash a led
      if (numsecs != numsecsold) {
        digitalWrite(ledgreen, HIGH);
        numsecsold = numsecs;
      } else {
        digitalWrite(ledgreen, LOW);
      }
    } else {
      numsecs = 0;
    }
  }
}  //end of void

void printprint(byte choose) {
  switch (choose) {
    case 0:
      printsatdata;  //current heading
      break;
    case 1: //speed and distance
      spddistdata();
      break;
  }
} //end of void

void  printsatdata() {
  
} //end of void

void spddistdata() {
  Serial.print(" sw1val ");
  Serial.print(sw1val);
  Serial.print(" sw1mode ");
  Serial.println(sw1mode);
}  //end of void

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Uno/Ultimate GPS Timing problem

Post by adafruit_support_mike »

The core problem is that you're using SoftwareSerial. That disables interrupts while it's communicating, so the millis() clock won't update and the microcontroller will ignore external interrupts like the PPS pulse. Statistically, you're bound to have collisions every so often, with the probability increasing the more you use the connection.

Try moving the GPS module over to the hardware Serial pins and see if that improves things.

User avatar
speedobob
 
Posts: 19
Joined: Sun Dec 28, 2014 7:11 pm

Re: Uno/Ultimate GPS Timing problem

Post by speedobob »

Yeah that's the conclusion I came to as well. Nice to get your explanation. I shall avoid getting myself in this situation in the future.
I've done away with all the conflict (I hope) by installing an external Real Time Clock (DS1307) and just monitoring its PPS output. Because of the conflicts, second to second time is not reliable but I don't care too much about that. The external PPS drives a one pulse per second 'tick' pulse plus a counter and I can derive all the timing I need from these two. I tested it for 10 hours today (partly against a stop watch) and it's accurate to a second. Perfect.
Seems ironic that we have some of the most accurate clocks known to man aboard all these GPS satellites and yet I can't derive a one pulse per second timer from them!!
Thanks very much for your help.
Bob

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”